zoukankan      html  css  js  c++  java
  • 《网络编程》套接字地址与名字转换

    前言

            在套接字编程中。我们常常使用数字的 IP 地址和port号进程编程,可是我们寻常所熟悉的是一些便于记忆的字符名字,要使这样的名字可以为套接字操作函数识别,所以这两者之间必须存在着某种转换关系。本节介绍的是 【主机名地址】 和 【服务名port号】 之间的转换。

    在 Unix 系统中。能够使用函数 gethostbyname、gethostbyaddr 实现【主机名 与 地址】之间的转换;能够使用函数 getservbyname、getservbyport 实现 【服务名 与 port号】 之间的转换。可是前面这些函数仅仅适合在 IPv4 域里面,若要在 IPv4 和 IPv6 实现这些功能。则能够使用 getaddrinfo 函数。有关这些函数在套接字编程中的使用可在前面文章查看《基于套接字编程

    主机名地址 之间的转换

    gethostbyname 与  gethostbyaddr 函数

    /* 主机名与地址之间转换 */
    
    /*
     * 函数功能:主机名与地址之间转换。
     * 返回值:若成功则返回主机结构指针。若出错则返回NULL;
     * 函数原型:
     */
    #include <netdb.h>
    
    struct hostent *gethostbyname(const char *hostname);//将主机名转换为数字地址。
    struct hostent *gethostaddr(const char *addr, size_t len, int family);//将数字地址转换为主机名。
    
    /* 函数功能:获取主机信息;
     * 函数原型:
     */
    struct hostent *gethostent(void);/* 获取主机信息。并返回hostent结构指针 */
    void sethostent(int stayopen);/* 设置主机信息 */
    void endhostent(void);
    /*
     * 说明:
     * 若主机数据文件没有打开。gethostent会打开它,该函数返回文件的下一条目;
     * 函数sethostent会打开文件。若文件已打开,那么将其回绕;
     * 函数endhostent将关闭文件。
     * 当中hostent结构至少包括例如以下成员数据:
     */
    struct hostent
    {
        char    *h_name;        /* official name of host */
        char    **h_aliases;    /* pointer to alternate host name array */
        int     h_addrtype;     /* address type: AF_INET */
        int     h_length;       /* length in bytes of address: 4 */
        char    **h_addr_list;  /* pointer to array of IPv4 address */
    };
    

            上面的函数若成功调用。则会返回一个指向 hostent 结构的指针,若出错则返回 NULL,且设置全局变量 h_error 为对应值。一般的 socket 系统调用都将错误信息存储在全局变量 error 中。可是和主机 host 有关的系统调用。则将错误信息存储在 h_error 中,它的取值例如以下:

    1. HOST_NOT_FOUND:找不到主机。
    2. TRY_AGAIN:重试;
    3. NO_RECOVERY:不可修复性错误;
    4. NO_DATA:指定的名字有效,可是没有记录。
    当中,hostent 结构信息之间关系例如以下图所看到的:


    服务名port号 之间的转换

    getservbyname 与 getservbyport 函数 

    /* 服务名与端口号之间的转换 */
    /*
     * 函数功能:服务名与端口号之间的转换;
     * 返回值:若成功则返回指针,若出错则返回NULL。
     * 函数原型:
     */
    #include <netdb.h>
    struct servent *getservbyname(const char *servname, const char *protoname);
    struct servent *getservbyport(int port, const char *protoname);
    struct servent *getservent(void);
    
    void setservent(int stayopen);
    void endservent(void);
    /*
     * protoname參数若为空,则返回取决与实现,若为非空。则指定协议名称。
     *
     * 当中servent 结构至少包括下面成员:
     */
    struct servent
    {
        char    *s_name;        /* official service name */
        char    **s_aliases;    /* pointer to alternate service name array */
        int     s_port;         /* port number */
        char    *s_proto;       /* name of protocol */
    };
    

    下面是採用上面函数编写的client程序:

    #include	"unp.h"
    
    int
    main(int argc, char **argv)
    {
    	int					sockfd, n;
    	char				recvline[MAXLINE + 1];
    	struct sockaddr_in	servaddr;
    	struct in_addr		**pptr = NULL;
    	struct in_addr		*inetaddrp[2];
    	struct in_addr		inetaddr;
    	struct hostent		*hp;
    	struct servent		*sp;
    
    	if (argc != 3)
    		err_quit("usage: %s <hostname> <service>", argv[0]);
    
        /* 将主机名作为gethostbyname的參数,并获取hostent结构信息 */
    	if ( (hp = gethostbyname(argv[1])) == NULL) {
            /* 若gethostbyname获取失败,则使用inet_aton构造地址单元素列表 */
    		if (inet_aton(argv[1], &inetaddr) == 0) {
    			err_quit("hostname error for %s: %s", argv[1], hstrerror(h_errno));
    		} else {
    			inetaddrp[0] = &inetaddr;
    			inetaddrp[1] = NULL;
    			pptr = inetaddrp;
    		}
    	} else {/* 若gethostbyname成功,则pptr指向地址链表 */
    		pptr = (struct in_addr **) hp->h_addr_list;
    	}
    
        /* 将服务名作为getservbyname的參数,获取servent结构信息 */
    	if ( (sp = getservbyname(argv[2], "tcp")) == NULL)
            /* 若失败则退出 */
    		err_quit("getservbyname error for %s", argv[2]);
    
        /* 遍历地址结构列表中的每个地址 */
    	for ( ; *pptr != NULL; pptr++) {
            /* 创建基于TCP套接字 */
    		if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
                err_sys("socket error");
    
            /* 初始化server地址信息 */
    		bzero(&servaddr, sizeof(servaddr));
    		servaddr.sin_family = AF_INET;
    		servaddr.sin_port = sp->s_port;/* 端口号由getservbyname获取 */
            /* 复制server地址信息,该地址信息由gethostbyname获取 */
    		memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
            /* 下面对地址列表中的每个server地址尝试连接 */
    		printf("trying %s
    ",
    			   Sock_ntop((SA *) &servaddr, sizeof(servaddr)));
    
    		if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) == 0)
    			break;		/* success */
            /* 若地址连接失败,则关闭与该地址相关的套接字 */
    		err_ret("connect error");
    		close(sockfd);
    	}
        /* 检查是否全部server地址都连接失败 */
    	if (*pptr == NULL)
    		err_quit("unable to connect");
    
        /* 处理函数,读取server应答信息,并显示到标准输出 */
    	while ( (n = Read(sockfd, recvline, MAXLINE)) > 0) {
    		recvline[n] = 0;	/* null terminate */
    		Fputs(recvline, stdout);
    	}
    	exit(0);
    }
    


    【地址 与 主机名】 和 【服务名 与 port号】之间的转换

    getaddrinfo 函数

    /* IPv6、IPv4 都可使用 */
    /*
     * 函数功能:将 服务名与port号 和 主机名与地址 之间转换。
     * 返回值:若成功则返回0。若出错则返回非0错误编码;
     * 函数原型:
     */
    #include <netdb.h>
    #include <sys/socket.h>
    int getaddrinfo(const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result);
    
    void freeaddrinfo(struct addrinfo *ai);/* 把从getaddrinfo函数动态分配的成员结构内存返回给系统,參数ai是由函数getaddrinfo返回的第一个addrinfo结构 */
    
    const char *gai_strerror(int error);//若getaddrinfo出错时,错误消息仅仅能由该函数输出;
    
    /*
     * 说明:
     * 该函数须要提供主机名或服务名,若仅仅提供当中一个,则还有一个必须指定为NULL;
     * addrinfo是一个结构链表。其定义例如以下:
     */
    struct addrinfo
    {
        int         ai_flags;       /* customize behavior */
        int         ai_family;      /* address family */
        int         ai_socktype;    /* socket type */
        int         ai_protocol;    /* protocol */
        socklen_t   ai_addrlen;     /* length in bytes of address */
        struct sockaddr *ai_addr;   /* address */
        char        *ai_canonname;  /* canonical name of host */
        struct addrinfo *ai_next;   /* next in list */
    };
    
    /*
     * 函数功能:将地址转换成服务名或主机名。
     * 返回值:若成功则返回0,若出错则返回非0值。
     * 函数原型:
     */
    #include <netdb.h>
    #include <sys/socket.h>
    int getnameinfo(const struct sockadd *addr, socklen_t alen, char * host, socklen_t hostlen,
            char * service, socklen_t servlen, unsigned int flags);
    
    /*
     * 说明:
     * addrinfo结构成员:
     * ai_flags 取值例如以下:
     * (1)AI_PASSIVE      套接字将用于被动打开;
     * (2)AI_CANONNAME    告知getaddrinfo函数返回主机的规范名字;
     * (3)AI_NUMERICHOST  防止不论什么类型的名字到地址映射,hostname必须是一个地址串。
     * (4)AI_NUMERICSERV  防止不论什么类型的名字到服务映射。service必须是一个十进制port号数串;
     * (5)AI_V4MAPPED     若同一时候指定ai_family值为AF_INET6。若没有可用的AAAA记录,则返回与A记录相应的IPv4映射的IPv6地址;
     * (6)AI_ALL          若同一时候指定AI_V4MAPPED标志,除了返回与AAAA记录相应的IPv6地址外,还返回与A记录相应的IPv4映射的IPv6地址;
     * (7)AI_ADDRCONFIG   依照所在主机的配置选择返回地址类型。
     */
    


    參考资料:

    《Unix 网络编程》

  • 相关阅读:
    tomcat指定运行jdk
    阿里技术面试1
    关于eclipse配置tomcat时,console打印成功消息,但是不能成功访问页面的问题
    websocket需要tomcat8.5.8以上版本才支持
    记一次未解决的异常:java.lang.NoClassDefFoundError: net/sf/json/JSONObject
    曾国藩的修身之道
    @Param的用法和作用
    java集合性能
    springmvc映射html文件以及解决乱码问题
    【redis】--配置
  • 原文地址:https://www.cnblogs.com/liguangsunls/p/6872887.html
Copyright © 2011-2022 走看看