Linux網路程式設計中常見的陷阱
1、謹慎處理Linux訊號量和錯誤號
首先介紹兩個很常用網路程式設計的用法,
(1)很多套接字程式中為了防止程序收到SIGPIPE訊號時崩潰往往會在程序中設定忽略訊號SIGPIPE。
(2)但是很多程式為了提高send的成功率對send做了二次封裝,即對send的返回值進行判斷,如果返回-1則進一步對Linux系統錯誤號errno進行判斷,如果errno==EINTR則繼續send,errno==EINTR表示程序在send過程中捕獲到系統訊號且系統訊號處理函式處理已經完成,但是這時send的套接字可能並沒有任何問題所以可以繼續使用。
以上兩個處理方法單獨看都沒有問題,都能提高程式的魯棒性,但是如果結合在一起卻給自己的程式挖了個陷阱,問題在於當send的對端已經關閉時send第一次會返回一個RST訊號,這個訊號一般都沒有處理繼續send,這時會收到一個SIGPIPE訊號,這個訊號已經被第一條忽略所以程序不會崩潰,但是send會返回-1且系統會將errno置為錯誤號EINTR,這時第二條中判斷到errno==EINTR會繼續send如此則會造成一直在send的死迴圈。
解決方法可以對send設定超時機制,一段時間send補成功則退出,從而避免死迴圈。
2、慎用SO_REUSEADDR套接字選項
《Unix網路程式設計》卷一對該選項是這樣描述的:
1、當有一個有相同本地地址和埠的socket1處於TIME_WAIT狀態時,而你啟
動的程式的socket2要佔用該地址和埠,你的程式就要用到該選項。
2、SO_REUSEADDR允許同一port上啟動同一伺服器的多個例項(多個程序)。但
每個例項繫結的IP地址是不能相同的。在有多塊網絡卡或用IP Alias技術的機器可
以測試這種情況。
3、SO_REUSEADDR允許單個程序繫結相同的埠到多個socket上,但每個soc
ket繫結的ip地址不同。這和2很相似,區別請看UNPv1。
4、SO_REUSEADDR允許完全相同的地址和埠的重複繫結。但這隻用於UDP的
多播,不用於TCP。
在linux上即便設定了SO_REUSEADDR屬性也是絕對不允許埠的完全重複繫結的,並且報EADDRINUSE的系統錯誤。
但是windows和linux機制不一樣,通過查閱微軟官網資料確實如此,原文如下:
Windows在設定了SO_REUSEADDR屬性後是允許完全重複繫結的,那麼windows平臺有沒有辦法避免埠衝突呢?答案是肯定的,windows為了解決這個問題引入了SO_EXCLUSIVEADDRUSE屬性,設定了SO_EXCLUSIVEADDRUSE屬性後後面繫結到同一埠的行為都將失敗。下面是微軟官網列出的套接字在預設情況、設定SO_REUSEADDR屬性和設定SO_EXCLUSIVEADDRUSE屬性後然後重複繫結某個埠的各種情況:(wildcard表示INADDR_ANY這類通用地址,Specific表示特定地址)
注:此表格情況適用於Windows Server 2003或者以上的系統且同一使用者呼叫設定。
由上圖可知:要在INADDR_ANY通用地址上使用某個埠並保證不會與其他應用衝突時需要設定SO_EXCLUSIVEADDRUSE屬性。但是尤其需要注意的是如果設定了SO_EXCLUSIVEADDRUSE屬性那麼處於TIME_WAIT狀態的套接字的埠將不能被繫結成功。