一、在前面講過的最簡單的回射客戶/伺服器程式中,一個用戶端即一個程序,隻會發起一個連接配接,隻要稍微修改一下就可以讓一個用戶端發起多個連接配接,然後隻利用其中一個連接配接發送資料。
先來認識一個函數getsockname
#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
利用此函數可以得到某連接配接sockfd的位址資訊,如ip位址和端口,這可以幫助我們判斷發起了多少個連接配接。
我們假設一個用戶端發起了5個連接配接,如下圖:

此時根據以前說過的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退出程序時,很可能會産生僵屍程序,如下圖:
最簡單的辦法就是父程序直接忽略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》