天天看点

linux网络编程之socket(七):一个进程发起多个连接和gethostbyname等函数

一、在前面讲过的最简单的回射客户/服务器程序中,一个客户端即一个进程,只会发起一个连接,只要稍微修改一下就可以让一个客户端发起多个连接,然后只利用其中一个连接发送数据。

先来认识一个函数getsockname

  #include <sys/socket.h>

  int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

利用此函数可以得到某连接sockfd的地址信息,如ip地址和端口,这可以帮助我们判断发起了多少个连接。

我们假设一个客户端发起了5个连接,如下图:

linux网络编程之socket(七):一个进程发起多个连接和gethostbyname等函数

此时根据以前说过的fork程序,服务器端会产生5个子进程对其进行服务。

修改过后的客户端程序如下:

在上述程序中,我们发起5个sock连接,但只是使用sock0通信,且利用getsockname 打印5个连接的信息。

先运行服务器程序,再运行客户端,输出如下:

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./echocli_5sock 

local ip=127.0.0.1 port=53094

local ip=127.0.0.1 port=53095

local ip=127.0.0.1 port=53096

local ip=127.0.0.1 port=53097

local ip=127.0.0.1 port=53098

ferwgeht

即每个连接的ip地址是一样的,但端口号不同,服务器方面通过accept返回的信息也打印出连接信息,如下:

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./echoser_recv_peek 

recv connect ip=127.0.0.1 port=53094

recv connect ip=127.0.0.1 port=53095

recv connect ip=127.0.0.1 port=53096

recv connect ip=127.0.0.1 port=53097

recv connect ip=127.0.0.1 port=53098

由于是多个连接,当客户端关闭而导致服务器子进程read 返回0退出进程时,很可能会产生僵尸进程,如下图:

linux网络编程之socket(七):一个进程发起多个连接和gethostbyname等函数

最简单的办法就是父进程直接忽略SIGCHLD信号,即signal(SIGCHLD, SIG_IGN);

如果我们想要捕获SIGCHLD信号的话,在信号处理函数中不能只调用一次wait/waitpid 函数,因为客户端退出发出FIN段的时机是不一定的,如果都能按一定时间顺序发送给5个服务器子进程,即子进程发生SIGCHLD信号给父进程的时间有前后之分,那handler函数会被调用多次,则是允许的,也不会产生僵尸进程;但当多个SIGCHLD信号同时到达,因为不可靠信号不能排队导致信号只保存一个,即其余信号会丢失,则产生的僵尸进程个数是不确定的,因为按前面所说取决于5个SIGCHLD信号到达的次序。解决的办法很简单,只要在handler函数中while 循环一下就ok 了,即使5个信号同时到达,只要接收到一个SIGCHLD信号,则5个子进程都会被清理掉,如下所示:

二、与前面说的getsockname 类似的函数还有getpeername、gethostname、gethostbyname、gethostbyaddr 、getaddrinfo、

 #include <unistd.h>

   int gethostname(char *name, size_t len);

 #include <netdb.h>

struct hostent *gethostbyname(const char *name);

gethostname 可以得到主机名,而gethostbyname 可以通过主机名得到一个结构体指针,可以通过此结构体得到与主机相关的ip地址信息等。

       The hostent structure is defined in <netdb.h> as follows:

           struct hostent {

               char  *h_name;            /* official name of host */

               char **h_aliases;         /* alias list */

               int    h_addrtype;        /* host address type */

               int    h_length;          /* length of address */

               char **h_addr_list;       /* list of addresses */

           }

           #define h_addr h_addr_list[0] /* for backward compatibility */

下面写个小程序测试一下:

输出如下:

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./getiplist 

127.0.1.1

local ip : 127.0.1.1

需要注意的是 hp->h_addr_list 是指针的指针,则hp->h_addr_list[i] 即指针,将其强制转换为struct in_addr 类型的指针,再通过

 inet_ntoa 函数转换成点分十进制的字符串,即 此语句 inet_ntoa(*(struct in_addr *)hp->h_addr_list[i]);  的意思。如果某主机配置了多个ip,则将输出

多个ip地址列表。

参考:

《Linux C 编程一站式学习》

《TCP/IP详解 卷一》

《UNP》