UNP卷1:第十一章(名字与地址转换)

最后更新于:2022-04-01 14:49:05

### 1. gethostbyname函数 ~~~ #include <netdb.h> struct hostent *gethostbyname( const char *hostname ); 返回:若成功则为非空指针,若出错则为NULL且设置h_errno ~~~ 而hostent的结构如下: ~~~ struct hostent{ char *h_name; char *h_aliases; int h_addrtype; int h_length; char **h_addr_list; }; ~~~ 书上的例子如下:(实际上在ubuntu系统上,gethostbyname似乎会出错) ~~~ #include "myunp.h" int main( int argc, char **argv ) { char *ptr, **pptr; char str[ INET_ADDRSTRLEN ]; struct hostent *hptr; while ( --argc > 0 ){ ptr = *++argv; if ( ( hptr = gethostbyname( ptr ) ) == NULL ){ printf("gethostbyname error for host:%s:%s\n", ptr, hstrerror( h_errno ) ); continue; } printf("official hostname:%s\n", hptr->h_name ); for ( pptr = hptr->h_aliases; *pptr != NULL; pptr++ ) printf("\talias:%s\n", *pptr ); switch( hptr->h_addrtype ){ case AF_INET: pptr = hptr->h_addr_list; for ( ; *pptr != NULL; pptr++ ) printf("\taddress name:%s\n", *pptr ); // printf("\taddress:%s\n", inet_ntop( hptr->h_addrtype, *pptr, str, sizeof(str))); break; default: printf("unknown address type"); break; } } exit(0); } ~~~ 程序输出: ~~~ root@ThinkPad-T430i:/home/leichaojian# cc hostent.c -o hostent root@ThinkPad-T430i:/home/leichaojian# ./hostent www.baidu.com official hostname:www.a.shifen.com alias:www.baidu.com address name:�a!kwww.�a!lifen.com address name:�a!lifen.com ~~~    所以执行inet_ntop时候程序直接报异常。 ### 2. 测试inet_ntop和inet_pton函数 ~~~ #include "myunp.h" 2 3 int main( void ) 4 { 5 char *ptr = "127.0.0.1"; 6 char str[ 1024 ]; 7 struct sockaddr_in servaddr; 8 9 bzero( &servaddr, sizeof( servaddr ) ); 10 servaddr.sin_family = AF_INET; 11 servaddr.sin_port = htons( SERV_PORT ); 12 inet_pton( AF_INET, ptr, &servaddr.sin_addr); 13 14 inet_ntop( AF_INET, ( SA * )&servaddr.sin_addr, str, sizeof( str ) ); 15 16 printf("%s\n", str ); 17 return 0; 18 } ~~~ 程序输出: ~~~ leichaojian@ThinkPad-T430i:~$ ./test 127.0.0.1 ~~~ ###  3. getservbyname和getservbyport函数    一个根据给定名字查找相应服务,一个给定端口号和可选协议查找相应服务。 ~~~ #include <stdio.h> #include <netdb.h> int main( int argc, char **argv ) { struct servent *sptr; sptr = getservbyname( "domain", "udp" ); sptr = getservbyname( "ftp", "tcp" ); sptr = getservbyname( "ftp", "NULL" ); sptr = getservbyname( "ftp", "udp" ); sptr = getservbyport( htons( 53 ), "udp" ); sptr = getservbyport( htons( 21 ), "tcp" ); sptr = getservbyport( htons( 21 ), "NULL" ); sptr = getservbyport( htons( 21 ), "udp" ); return 0; } ~~~ 程序输出: ~~~ (gdb) break 8 Breakpoint 1 at 0x4005dc: file getservbynameport.c, line 8. (gdb) r Starting program: /home/leichaojian/getservbynameport Breakpoint 1, main (argc=1, argv=0x7fffffffde58) at getservbynameport.c:8 8 sptr = getservbyname( "domain", "udp" ); (gdb) n 9 sptr = getservbyname( "ftp", "tcp" ); (gdb) p *sptr $1 = {s_name = 0x602010 "domain", s_aliases = 0x602020, s_port = 13568, s_proto = 0x60201b "udp"} (gdb) n 10 sptr = getservbyname( "ftp", "NULL" ); (gdb) p *sptr $2 = {s_name = 0x602010 "ftp", s_aliases = 0x602020, s_port = 5376, s_proto = 0x602018 "tcp"} (gdb) n 11 sptr = getservbyname( "ftp", "udp" ); (gdb) p *sptr Cannot access memory at address 0x0 (gdb) n 13 sptr = getservbyport( htons( 53 ), "udp" ); (gdb) p *sptr Cannot access memory at address 0x0 (gdb) n 14 sptr = getservbyport( htons( 21 ), "tcp" ); (gdb) p *sptr $3 = {s_name = 0x603290 "domain", s_aliases = 0x6032a0, s_port = 13568, s_proto = 0x60329b "udp"} (gdb) n 15 sptr = getservbyport( htons( 21 ), "NULL" ); (gdb) p *sptr $4 = {s_name = 0x603290 "ftp", s_aliases = 0x6032a0, s_port = 5376, s_proto = 0x603298 "tcp"} (gdb) n 16 sptr = getservbyport( htons( 21 ), "udp" ); (gdb) p *sptr Cannot access memory at address 0x0 (gdb) n 18 return 0; ~~~ ### 4. getaddrinfo函数    此函数可用来代替gethostbyname和gethostbyaddr。 ~~~ #include <netdb.h> int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result ); 返回:若成功则为0,若出错则为非0 ~~~ 参数1为主机名或地址串(点分十进制数串),service参数是一个服务名或十进制端口号数串,hints目前就直接置为NULL,而result则是我们需要的信息: ~~~ struct addrinfo{ int ai_flags; int ai_family; int ai_socktype; int ai_addrlen; socklen_t ai_addrlen; char *ai_canonname; struct sockaddr *ai_addr; struct addrinfo *ai_next; }; ~~~ 测试用例如下: ~~~ #include <stdio.h> #include <netdb.h> int main( int argc, char **argv ) { struct addrinfo hints, *res; struct sockaddr_in *addr; char str[ 1024 ]; getaddrinfo( "ThinkPad-T430i", "domain", NULL, &res ); for ( ;res->ai_next;res = res->ai_next ){ printf("ai_falgs:%d\n", res->ai_flags); printf("ai_family:%d\n", res->ai_family); printf("ai_socktype:%d\n", res->ai_socktype); printf("ai_addrlen:%d\n", res->ai_addrlen); printf("ai_canonname:%s\n", res->ai_canonname); addr = ( struct sockaddr_in * )res->ai_addr; printf("sin_family:%d\n", addr->sin_family); printf("sin_port:%d\n", ntohs( addr->sin_port ) ); inet_ntop( addr->sin_family, &addr->sin_addr, str, sizeof( str ) ); printf("sin_addr:%s\n", str); } return 0; } ~~~ 程序输出: ~~~ leichaojian@ThinkPad-T430i:~$ cc getaddrinfo.c leichaojian@ThinkPad-T430i:~$ ./a.out ai_falgs:40 ai_family:2 ai_socktype:1 ai_addrlen:16 ai_canonname:(null) sin_family:2 sin_port:53 sin_addr:127.0.1.1 ~~~ ### 5. 使用getaddrinfo来完成TCP时间获取服务器程序 ### 1)时间获取客户程序 ~~~ #include <stdio.h> #include <netdb.h> #include <sys/socket.h> #define MAXLINE 1024 int tcp_connect( const char *host, const char *serv ); int main( int argc, char **argv ) { int sockfd, n; char recvline[ MAXLINE + 1 ]; socklen_t len; struct sockaddr_in cliaddr; if ( argc != 3 ){ printf("argument should be 3\n"); exit(1); } sockfd = tcp_connect( argv[1], argv[2]); len = sizeof( cliaddr ); getpeername( sockfd, ( struct sockaddr * )&cliaddr, len ); inet_ntop( AF_INET, &cliaddr.sin_addr, recvline, sizeof( recvline ) ); printf("connect to %s\n", recvline); while ( ( n = read( sockfd, recvline, MAXLINE )) > 0 ){ recvline[ n ] = 0; fputs( recvline, stdout ); } exit( 0 ); } int tcp_connect( const char *host, const char *serv ) { int sockfd, n; struct addrinfo hints, *res, *ressave; struct sockaddr_in *cliaddr; bzero( &hints, sizeof( struct addrinfo ) ); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if ( ( n = getaddrinfo( host, serv, &hints, &res ) ) != 0 ){ printf("tcp_connect error for %s,%s:%s", host, serv, gai_strerror(n)); exit(1); } ressave = res; do{ sockfd = socket( res->ai_family, res->ai_socktype, res->ai_protocol ); if ( sockfd < 0 ) continue; if ( connect( sockfd, res->ai_addr, res->ai_addrlen ) == 0 ) break; //用于调试 cliaddr = ( struct sockaddr_in * )res->ai_addr; close( sockfd ); } while ( ( res = res->ai_next ) != NULL ); if ( res == NULL ) printf("tcp_connect error for %s,%s\n", host, serv ); freeaddrinfo( ressave ); return ( sockfd ); } ~~~ 程序输出: ~~~ leichaojian@ThinkPad-T430i:~$ ./newdaytimetcpcli ThinkPad-T430i daytime connect to 0.0.0.0 30 SEP 2014 00:21:58 CST ~~~ 备注:这里要开启时间,具体操作如下 1) 安装 ~~~ leichaojian@ThinkPad-T430i:~$ sudo apt-get install xinetd ~~~ 2) 修改配置如下: ~~~ leichaojian@ThinkPad-T430i:~$ cd /etc/xinetd.d leichaojian@ThinkPad-T430i:/etc/xinetd.d$ vim daytime ~~~ 3) 具体修改: 将disable=yes改为disable=no(两处均要修改) 4)我不知道如何重新载入,所以直接重启了电脑。 ### 2)时间获取服务器程序 ~~~ #include <stdio.h> #include <netdb.h> #include <sys/socket.h> #include <time.h> #define MAXLINE 1024 int tcp_listen( const char *host, const char *serv, socklen_t *addrlenp ); int main( int argc, char **argv ) { int listenfd, connfd; socklen_t len; char buff[ MAXLINE ]; time_t ticks; struct sockaddr_in cliaddr; listenfd = tcp_listen( argv[ 1 ], argv[ 2 ], NULL ); for ( ; ; ){ len = sizeof( cliaddr ); connfd = accept( listenfd, ( struct sockaddr *)&cliaddr, &len ); inet_ntop( AF_INET, &cliaddr.sin_addr, buff, sizeof( buff ) ); printf("conneciton from %s\n", buff ); ticks = time( NULL ); snprintf( buff, sizeof( buff ), "%.24s\r\n", ctime(&ticks)); write( connfd, buff, strlen(buff)); close( connfd ); } } int tcp_listen( const char *host, const char *serv, socklen_t *addrlenp ) { int listenfd, n; const int on = 1; struct addrinfo hints, *res, *ressave; bzero( &hints, sizeof( struct addrinfo ) ); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if ( ( n = getaddrinfo( host, serv, &hints, &res ) ) != 0 ){ printf("tcp_listen error for %s,%s: %s\n", host, serv, gai_strerror(n)); } ressave = res; do{ listenfd = socket( res->ai_family, res->ai_socktype, res->ai_protocol ); if ( listenfd < 0 ) continue; setsockopt( listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if ( bind( listenfd, res->ai_addr, res->ai_addrlen) == 0 ) break; close( listenfd ); } while(( res = res->ai_next) != NULL ); if ( res == NULL ){ printf("tcp_listen error for %s,%s", host, serv); } listen( listenfd, 5 ); if ( addrlenp ) *addrlenp = res->ai_addrlen; freeaddrinfo(ressave); return (listenfd ); } ~~~ 服务端运行: ~~~ leichaojian@ThinkPad-T430i:~$ ./newdaytimetcpserv ThinkPad-T430i 9877 conneciton from 127.0.0.1 ~~~ 客户端运行: ~~~ leichaojian@ThinkPad-T430i:~$ ./newdaytimetcpcli ThinkPad-T430i 9877 connect to 0.0.0.0 Tue Sep 30 18:14:25 2014 ~~~ ### 6. 使用getaddrinfo来完成UDP时间获取服务器程序 ### 1)时间获取客户端程序 ~~~ #include <stdio.h> #include <netdb.h> #include <sys/socket.h> #define MAXLINE 1024 int udp_client(const char *host, const char *serv, struct sockaddr **saptr, socklen_t *lenp ); int main(int argc, char **argv) { int sockfd, n; char recvline[ MAXLINE + 1 ]; socklen_t salen; struct sockaddr *sa; if (argc != 3){ printf("argument should be 3\n"); exit(1); } sockfd = udp_client(argv[1], argv[2], (void **)&sa, &salen); sendto(sockfd, "", 1, 0, sa, salen); n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL); recvline[n] = '\0'; fputs(recvline, stdout); exit(0); } int udp_client(const char *host, const char *serv, struct sockaddr **saptr, socklen_t *lenp ) { int sockfd, n; struct addrinfo hints, *res, *ressave; bzero(&hints, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; if ((n = getaddrinfo(host, serv, &hints, &res)) != 0){ printf("udp_client error for %s,%s:%s\n", host, serv, gai_strerror(n)); exit(1); } ressave = res; do{ sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sockfd >= 0) break; }while ((res = res->ai_next) != NULL); if (res == NULL){ printf("udp_client error for %s,%s\n", host, serv); exit(1); } *saptr = malloc(res->ai_addrlen); memcpy(*saptr, res->ai_addr, res->ai_addrlen); *lenp = res->ai_addrlen; freeaddrinfo(ressave); return (sockfd); } ~~~ 程序输出: ~~~ leichaojian@ThinkPad-T430i:~$ ./newdaytimeudpcli ThinkPad-T430i daytime 30 SEP 2014 18:37:24 CST ~~~ ### 2) 时间获取服务端程序 ~~~ #include <stdio.h> #include <netdb.h> #include <sys/socket.h> #include <time.h> #define MAXLINE 1024 int udp_server(const char *host, const char *serv, socklen_t *addrlenp); int main(int argc, char **argv) { int sockfd; ssize_t n; char buff[MAXLINE]; time_t ticks; socklen_t len; struct sockaddr_in cliaddr; sockfd = udp_server( argv[1], argv[2], NULL); for ( ; ; ){ len = sizeof(cliaddr); n = recvfrom(sockfd, buff, MAXLINE, 0, (struct sockaddr *)&cliaddr, &len); inet_ntop(AF_INET, &cliaddr.sin_addr, buff, sizeof(buff)); printf("datagram from %s\n", buff ); ticks = time(NULL); snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); sendto(sockfd, buff, strlen(buff), 0, (struct sockaddr *)&cliaddr, len); } } int udp_server(const char *host, const char *serv, socklen_t *addrlenp) { int sockfd, n; struct addrinfo hints, *res, *ressave; bzero(&hints, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; if ((n = getaddrinfo(host, serv, &hints, &res)) != 0){ printf("udp_server error for %s,%s:%s", host, serv, gai_strerror(n)); exit(1); } ressave = res; do{ sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sockfd < 0) continue; if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0) break; close(sockfd); }while ((res = res->ai_next) != NULL); if (res == NULL){ printf("udp_server error for %s,%s\n", host, serv); exit(1); } if (addrlenp) *addrlenp = res->ai_addrlen; freeaddrinfo(ressave); return (sockfd); } ~~~ 服务器运行: ~~~ leichaojian@ThinkPad-T430i:~$ ./newdaytimeudpserv ThinkPad-T430i 9877 datagram from 127.0.0.1 ~~~ 客户端运行: ~~~ leichaojian@ThinkPad-T430i:~$ ./newdaytimeudpcli ThinkPad-T430i 9877 Tue Sep 30 18:56:41 2014 ~~~
';