1. Socket ack (acknowledgement)
Socket ack是指當socket接收到數(shù)據(jù)之后,發(fā)送一個ack字符串(比如$ACK)給socket發(fā)送方。這樣,socket發(fā)送方可以根據(jù)是否收到了ack判斷對方是否收到了數(shù)據(jù)。
Socket ack是顯示的在應用程序中加入的一種通訊協(xié)議。如果不使用ack,在socket通訊中,可能會丟失數(shù)據(jù)。
比如,socket client要連續(xù)的給socket server發(fā)送100條消息。如果我們在server收到第50條消息的時候,強行kill了server。那么查詢client端發(fā)送的log,可能client端成功發(fā)送了51條。只有當client端發(fā)送第52條消息的時候才遇到異常。這樣第51條消息就丟失了。
所以為了確保數(shù)據(jù)傳輸?shù)臏蚀_性,我們可以引入ack協(xié)議。有時我們不僅要確保server不但收到了數(shù)據(jù),而且還要保證server成功處理了數(shù)據(jù)。這時,可以等server成功處理完數(shù)據(jù)之后,再給client發(fā)ack。
2. Socket Keep Alive
Socket連接像數(shù)據(jù)庫連接一樣,屬于重量型資源。如果我們頻繁的創(chuàng)建socket、發(fā)送/接收數(shù)據(jù)、關(guān)閉socket,那么會有很大一部分時間浪費在socket的創(chuàng)建和關(guān)閉上。
所以,如果我們經(jīng)常需要與同一個socket地址發(fā)送/接收數(shù)據(jù)時,應該考慮只創(chuàng)建一次socket,然后一直使用這個socket對象發(fā)送/接收數(shù)據(jù)。
3. Heartbeat
通常,我們會設置socket的receive timeout。這樣,如果我們一直打開著socket (keep alive), 而很長時間又沒有數(shù)據(jù)通訊,socket接收方就會timeout,最終導致打開的連接壞掉。
如果很長時間沒有數(shù)據(jù)通訊,防火墻或代理服務器也可能會關(guān)閉打開的socket連接。
所以為了保證打開的socket連接一直可用,如果一段時間沒有數(shù)據(jù)進行通訊(或指定一個時間間隔),我們可以顯示的發(fā)送一個heartbeat消息(比如: $HRT)給對方,從而保證連接不會被異常關(guān)閉。
4. Socket Close
每一個socket對象會持有一個socket descriptor (其實就是file descriptor),操作系統(tǒng)對于socket descriptor有一個最大限制。因此當socket不再使用時,一定要記得關(guān)閉,即使socket連接失敗或出現(xiàn)異常,只要socket對象不為null,一定要記得關(guān)閉。
下面圖顯示了,當socket關(guān)閉時,socket的狀態(tài)變化(socket狀態(tài)可以通過netstat命令查看)。更詳細的解釋,可以google一下。

當主動一方調(diào)用close(先調(diào)用close)時的狀態(tài)變化:
ESTABLISHED -> FIN_WAIT_1-> FIN_WAIT_2 -> TIME_WAIT -> CLOSED
當被動一方調(diào)用close(后調(diào)用close)時的狀態(tài)變化:
ESTABLISHED -> CLOSE_WAIT -> LAST_ACK -> CLOSED
通常,TIME_WAIT 是正常狀態(tài),過一段時間(2MSL, 1到4分鐘)就會自動消失.
我們需要特別注意CLOSE_WAIT 狀態(tài):
1. 如果很長時間才消失,表明socket server處理太慢,很多client已經(jīng)連接到server,發(fā)送完數(shù)據(jù)并close了。
2. 如果一直也不消失,表明有socket沒有正常close (對方已經(jīng)close了)
5. SO_REUSEADDR Option
當socket主動調(diào)用close的時候,從上面可以知道,它最終會進入TIME_WAIT 狀態(tài),需要過1到4分鐘,才能完全close。
當socket處于TIME_WAIT 狀態(tài)時,它仍然占用正在使用的IP/PORT。這樣,如果我們的程序(比如socket server)使用了一個固定的IP/PORT,當socket處于TIME_WAIT 狀態(tài)時,程序?qū)⒉荒芰⒓粗貑ⅲ瑫霈F(xiàn)端口占用錯誤。
Socket提供了一個setReuseAddress()方法,可以設置當socket處于TIME_WAIT 狀態(tài)時,是否允許其它進程綁定這個端口。
如果我們正在開發(fā)socket server,一定要記得調(diào)用ServerSocket.setReuseAddress(true).
Client socket也有這個方法,而且有時可能需要指明client連接server時所使用的本地IP/PORT(一般不用指明,系統(tǒng)會隨機選擇一個PORT)。但實際測試,在client socket上設置這個方法在Windows和Solaris下并不起作用。當socket處于TIME_WAIT 狀態(tài)時,重啟client仍然出現(xiàn)端口占用錯誤。上網(wǎng)搜索了很長時間,很多人都碰到了這個問題,可能是操作系統(tǒng)底層socket實現(xiàn)問題。因為測試使用C語言開發(fā)的socket client,同樣也有這個錯誤。有人說LINUX下好用,還有就是可以嘗試修改tcp_time_wait_interval來減小TIME_WAIT等待時間