1) 線上程中碰到socket阻塞怎麼辦?
比如socket伺服器accept阻塞了,造成線程不能順利退出?
解決辦法通過socket的KeepAlive機制;
2) 如何檢測socket串連是否斷開或異常?
1)通過心跳機制;
2)KeepAlive機制;
3)Socket 常用的屬性
SO_REUSEADDR:
1、 當有一個有相同本地地址和連接埠的socket1處於TIME_WAIT狀態時,而你啟動的程式的socket2要佔用該地址和連接埠,你的程式就要用到該選項。
2、SO_REUSEADDR允許同一port上啟動同一伺服器的多個執行個體(多個進程)。但每個執行個體綁定的IP地址是不能相同的。在有多塊網卡或用IP Alias技術的機器可以測試這種情況。
3、SO_REUSEADDR允許單個進程綁定相同的連接埠到多個socket上,但每個socket綁定的ip地址不同。這和2很相似,區別請看UNPv1。
4、SO_REUSEADDR允許完全相同的地址和連接埠的重複綁定。但這隻用於UDP的多播,不用於TCP。
SO_RECVBUF/SO_SNDBUF
設定socket的接收和發送緩衝;
SO_RCVTIMEO/ SO_SNDTIMEO
設定socket的發送逾時和接收逾時,在阻塞socket中非常有用,避免一直阻塞;
SO_LINGER:
linger,顧名思義是延遲延緩的意思,這裡是延緩連線導向的socket的close操作。預設,close立即返回,但是當發送緩衝區中還有一部分資料的時候,系統將會嘗試將資料發送給對端。SO_LINGER可以改變close的行為。避免socket進入TIME_WAIT狀態;
SO_KEEPALIVE
SO_KEEPALIVE/TCP_KEEPCNT/TCP_KEEPIDLE/TCP_KEEPINTVL
如果一方已經關閉或異常終止串連,而另一方卻不知道,我們將這樣的TCP串連稱為半開啟的。TCP通過保活定時器(KeepAlive)來檢測半開啟串連。
在高並發的網路伺服器中,經常會出現漏掉socket的情況,對應的結果有一種情況就是出現大量的CLOSE_WAIT狀態的串連。這個時候,可以通過設定KEEPALIVE選項來解決這個問題,當然還有其他的方法可以解決這個問題。
TCP_DEFER_ACCEPT
defer accept,從字面上理解是延遲accept,實際上是當接收到第一個資料之後,才會建立串連。對於像HTTP等非互動伺服器,這個很有意義,可以用來防禦空串連攻擊(只是建立串連,但是不發送任何資料)。
使用方法如下:
val = 5;
setsockopt(srv_socket->fd, SOL_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val));
裡面 val 的單位是秒,注意如果開啟這個功能,kernel
在val
秒之內還沒有收到資料,不會繼續喚醒進程,而是直接丟棄串連。如果伺服器設定TCP_DEFER_ACCEPT選項後,伺服器受到一個CONNECT請求後,三向交握之後,新的socket狀態依然為SYN_RECV,而不是ESTABLISHED,作業系統不會Accept。
由於設定TCP_DEFER_ACCEPT選項之後,三向交握後狀態沒有達到ESTABLISHED,而是SYN_RECV。這個時候,如果用戶端一直沒有發送"資料"報文,伺服器將重傳SYN/ACK報文,重傳次數受net.ipv4.tcp_synack_retries參數控制,達到重傳次數之後,才會再次進行setsockopt中設定的逾時值,因此會出現SYN_RECV存留時間比設定值大一些的情況。
TCP_NODELAY/TCP_CHORK:
CP_NODELAY和TCP_CORK基本上控制了包的“Nagle化”,Nagle化在這裡的含義是採用Nagle演算法把較小的包組裝為更大的幀。TCP_NODELAY和TCP_CORK都禁掉了Nagle演算法,只不過他們的行為不同而已。
TCP_NODELAY 不使用Nagle演算法,不會將小包進行拼接成大包再進行發送,直接將小包發送出去,會使得小包時候使用者體驗非常好。
當在傳送大量資料的時候,為了提高TCP發送效率,可以設定TCP_CORK,CORK顧名思義,就是"塞子"的意思,它會盡量在每次發送最大的資料量。當設定了TCP_CORK後,會有阻塞200ms,當阻塞時間過後,資料就會自動傳送。
4) socket
串連狀態
CLOSED:無串連是活動的或進行中;
LISTEN:伺服器在等待進入呼叫;
SYN_RECV:一個串連請求已經到達,等待確認;
SYN_SENT:應用已經開始,開啟一個串連;
ESTABLISHED:正常資料轉送狀態;
FIN_WAIT1:應用說它已經完成;
FIN_WAIT2:另一邊已同意釋放;
ITMED_WAIT:等待所有分組死掉;
CLOSING:兩邊同時嘗試關閉;
TIME_WAIT:另一邊已初始化一個釋放;
LAST_ACK:等待所有分組死掉;