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
~~~