一、在前面講過的回射客戶/伺服器程式中,伺服器隻能處理一個用戶端的請求,如何同時服務多個用戶端呢?在未講到select/poll/epoll等進階IO之前,比較老土的辦法是使用fork來實作。網絡伺服器通常用fork來同時服務多個用戶端,父程序專門負責監聽端口,每次accept一個新的用戶端連接配接就fork出一個子程序專門服務這個用戶端。但是子程序退出時會産生僵屍程序,父程序要注意處理SIGCHLD信号和調用wait清理僵屍程序,最簡單的辦法就是直接忽略SIGCHLD信号。
上述程式利用了一點,就是父子程序共享打開的檔案描述符,因為在子程序已經用不到監聽描述符,故将其關閉,而連接配接描述符對父程序也沒價值,将其關閉。當某個用戶端關閉,則read 傳回0,退出循環,子程序順便exit,但如果沒有設定對SIGCHLD信号的忽略,則因為父程序還沒退出,故子程序會變成僵屍程序。
現在先運作server,再打開另外兩個終端,運作client(直接用回射客戶/伺服器程式中的用戶端程式),可以看到server輸出如下:
simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./echoser_fork
recv connect ip=127.0.0.1 port=46452
recv connect ip=127.0.0.1 port=46453
在另一個終端ps一下:
simba@ubuntu:~$ ps aux | grep echoser
simba 3300 0.0 0.0 2008 280 pts/0 S+ 22:10 0:00 ./echoser_fork
simba 3303 0.0 0.0 2008 60 pts/0 S+ 22:10 0:00 ./echoser_fork
simba 3305 0.0 0.0 2008 60 pts/0 S+ 22:10 0:00 ./echoser_fork
simba 3313 0.0 0.0 4392 836 pts/3 S+ 22:12 0:00 grep --color=auto echoser
simba@ubuntu:~$
發現共有3個程序,其中一個是父程序處于監聽中,另外兩個是子程序處于對用戶端服務中,現在ctrl+c 掉其中一個client,由上面的分析可知對應服務的子程序也會退出,而因為我們設定了父程序對SIGCHLD信号進行忽略,故不會産生僵屍程序,輸出如下:
simba 3321 0.0 0.0 4392 836 pts/3 S+ 22:13 0:00 grep --color=auto echoser
如果把29行代碼注釋掉,上述的情景輸出可能為:
simba 3125 0.0 0.0 2004 280 pts/0 S+ 21:38 0:00 ./echoser_fork
simba 3128 0.0 0.0 0 0 pts/0 Z+ 21:38 0:00 [echoser_fork] <defunct>
simba 3130 0.0 0.0 2004 60 pts/0 S+ 21:38 0:00 ./echoser_fork
simba 3141 0.0 0.0 4392 832 pts/3 S+ 21:40 0:00 grep --color=auto echoser
即子程序退出後變成了僵屍程序。
如果不想忽略SIGCHLD信号,則必須在信号處理函數中調用wait處理,但這裡需要注意的是wait隻能等待第一個退出的子程序,是以這裡需要使用
waitpid。若目前隻有一個子程序退出,則waitpid一次之後因為其他子程序狀态尚未改變,故傳回0退出循環;若幾個連接配接同時斷開,信号因為不能排隊
而隻接收到一個SIGCHLD信号,waitpid多次之後已經不存在子程序了,傳回-1退出循環。如下所示:
二、在最基本的回射客戶/伺服器程式中,伺服器隻能被動接收用戶端的資訊,而不能主動發送資訊給用戶端,如果我們想實作對等通信,即P2P,可以
在伺服器程式用使用兩個程序,一個程序接收使用者的輸入并發送給用戶端,另一個程序被動接收用戶端的消息并列印出來,此程序當read 傳回0 時得知
用戶端已經關閉需要退出程序,此時尚有另一個程序未退出,可以通過在退出前發送消息給它,在消息處理函數中退出。當然用戶端也必須使用雙進
程,原理與伺服器程式相同。
下面貼出伺服器端程式:
用戶端程式與伺服器端程式的改變差不多,就不貼了,這裡是使用父子程序來完成對等通信,即雙方都可以發送資訊給對方,也可以接收對方的資訊,
上面使用了kill 函數來發現自定義信号,如果子程序發送信号給父程序,可以使用getppid 函數得到父程序的id。輸出如下:
simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./p2pser
recv connect ip=127.0.0.1 port=56404
fsafd //<< server 輸入
fds
^C
simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./p2pcli
fsafd
fds //<< client 輸入
peer close
recv a sig=10
注意,ctrl+c 會将前台程序組中的兩個程序都終止掉,即父子程序都打開了conn,隻有兩個程序都close(conn),将file 的引用計數減為0,才會真正關
閉sockfs 檔案,這樣client 其中一個程序才能read 傳回0列印peer close,并發信号給另一個程序,在信号處理函數中退出。