天天看點

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》