在Java的Socket中,主要包含了如下可設置的TCP參數。java
屬性算法 |
說明編程 |
默認值服務器 |
SO_TIMEOUT網絡 |
對ServerSocket來講表示等待鏈接的最長空等待時間; 對Socket來講表示讀數據最長空等待時間。socket |
0測試 |
TCP_NODELAY字體 |
是否一有數據就立刻發送。this |
falsespa |
SO_LINGER |
優雅地關閉套接字,或者馬上關閉。 |
-1 |
SO_SNDBUF |
發送數據的緩衝區大小。 |
8K |
SO_RCVBUF |
接收數據的緩衝區大小。 |
8K |
SO_KEEPALIVE |
是否啓用心跳機制。 |
false |
SO_REUSEADDR |
是否地址重用。 |
false |
BACKLOG |
服務端處理線程全忙後,容許多少個新請求進入等待。 |
50 |
BACKLOG用於構造服務端套接字ServerSocket對象,標識當服務器請求處理線程全滿時,用於臨時存放已完成三次握手的請求的隊列的最大長度。若是未設置或所設置的值小於1,Java將使用默認值50。
ServerSocket serverSocket = new ServerSocket(8080, 100); |
在TCP/IP協議中,不管發送多少數據,老是要在數據前面加上協議頭,同時,對方接收到數據,也須要發送ACK表示確認。爲了儘量的利用網絡帶寬,TCP老是但願儘量的發送足夠大的數據。這裏就涉及到一個名爲Nagle的算法,該算法的目的就是爲了儘量發送大塊數據,避免網絡中充斥着許多小數據塊。
TCP_NODELAY選項,就是用於啓用或關於Nagle算法。若是要求高實時性,有數據發送時就立刻發送,就將該選項設置爲true關閉Nagle算法;若是要減小發送次數減小網絡交互,就設置爲false等累積必定大小後再發送。默認爲false。
Socket中操做該屬性的方法以下:
void setTcpNoDelay(boolean on) 啓用/禁用 TCP_NODELAY(啓用/禁用 Nagle 算法)。 boolean getTcpNoDelay() 測試是否啓用 TCP_NODELAY。 |
關於Nagle算法介紹,請參考附錄部分。
對於服務端套接字ServerSocket來講,SO_TIMEOUT表示服務端accept方法空等待客戶端鏈接的最長時間;對於客戶端套接字Socket來講,SO_TIMEOUT表示輸入流讀取數據read方法的最長等待時間。一旦超過設置的SO_TIMEOUT,程度將拋出超時異常。
ServerSocket/Socket中操做該屬性的方法以下:
int getSoTimeout() 返回 SO_TIMEOUT 的設置。 void setSoTimeout(int timeout) 啓用/禁用帶有指定超時值的 SO_TIMEOUT,以毫秒爲單位。 |
使用示例:
ServerSocket serverSocket = new ServerSocket(8080); serverSocket.setSoTimeout(30000);
Socket clientSocket = serverSocket.accept(); clientSocket.setSoTimeout(20000); |
當調用closesocket關閉套接字時,SO_LINGER將決定系統如何處理殘存在套接字發送隊列中的數據。處理方式無非兩種:丟棄或者將數據繼續發送至對端,優雅關閉鏈接。事實上,SO_LINGER並不被推薦使用,大多數狀況下咱們推薦使用默認的關閉方式(即下方表格中的第一種狀況)。
下方代碼段顯示linger結構語法,表格爲不一樣參數狀況下的套接字行爲。
typedef struct linger { u_short l_onoff; //開關,零或者非零 u_short l_linger; //優雅關閉最長時限 } linger; |
各字段與對應行爲以下表所示。
l_onoff |
l_linger |
closesocket行爲 |
發送隊列 |
底層行爲 |
零 |
忽略 |
當即返回。 |
保持直至發送完成。 |
系統接管套接字並保證將數據發送至對端。 |
非零 |
零 |
當即返回。 |
當即放棄。 |
直接發送RST包,自身當即復位,不用通過2MSL狀態。對端收到復位錯誤號。 |
非零 |
非零 |
阻塞直到l_linger時間超時或數據發送完成。(套接字必須設置爲阻塞) |
在超時時間段內保持嘗試發送,若超時則當即放棄。 |
超時則同第二種狀況,若發送完成則皆大歡喜。 |
Socket中操做該屬性的方法以下:
void setSoLinger(boolean on, int linger) 啓用/禁用具備指定逗留時間(以秒爲單位)的SO_LINGER。 Linger最大取值爲65535。 int getSoLinger() 返回 SO_LINGER 的設置。默認值爲-1。 |
因爲getSoLinger()方法返回的-1沒有太多意思,咱們查看到Java的默認實現PlainSocketImpl.c文件中,賦值操做代碼片斷以下所示。
/* * Class: java_net_PlainSocketImpl * Method: socketSetOption * Signature: (IZLjava/lang/Object;)V */ JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_socketSetOption(JNIEnv *env, jobject this, jint cmd, jboolean on, jobject value) { … switch (cmd) { case java_net_SocketOptions_SO_SNDBUF : case java_net_SocketOptions_SO_RCVBUF : case java_net_SocketOptions_SO_LINGER : case java_net_SocketOptions_IP_TOS : { … if (cmd == java_net_SocketOptions_SO_LINGER) { if (on) { optval.ling.l_onoff = 1; optval.ling.l_linger = (*env)->GetIntField(env, value, fid); } else { optval.ling.l_onoff = 0; optval.ling.l_linger = 0; } optlen = sizeof(optval.ling); } else { optval.i = (*env)->GetIntField(env, value, fid); optlen = sizeof(optval.i); }
break; }
/* Boolean -> int */ default : optval.i = (on ? 1 : 0); optlen = sizeof(optval.i);
} … } |
從藍色字體部分代碼能夠看出,只要賦值爲false,則底層linger結構中的l_onoff和l_linger的值均爲0,符合表中的第一種狀況。
發送緩衝區的大小設置,默認爲8K。
Socket中操做該屬性的方法以下:
void setSendBufferSize(int size) 將此 Socket 的 SO_SNDBUF 選項設置爲指定的值。 int getSendBufferSize() 獲取此 Socket 的 SO_SNDBUF 選項的值,該值是平臺在 Socket 上輸出時使用的緩衝區大小。 |
接收緩衝區大小設置,默認爲8K。該屬性既能夠在ServerSocket實例中設置,也能夠在Socket實例中設置。
ServerSocket/Socket中操做該屬性的方法以下:
void setReceiveBufferSize(int size) 將此 Socket 的 SO_RCVBUF 選項設置爲指定的值。 int getReceiveBufferSize() 獲取此 Socket 的 SO_RCVBUF 選項的值,該值是平臺在 Socket 上輸入時使用的緩衝區大小。 |
套接字自己是有一套心跳保活機制的,不過默認的設置並不像咱們一廂情願的那樣有效。在雙方TCP套接字創建鏈接後(即都進入ESTABLISHED狀態)而且在兩個小時左右上層沒有任何數據傳輸的狀況下,這套機制纔會被激活。
不少人認爲兩個小時的時間設置得很不合理。爲何不設置成爲10分鐘,或者更短的時間?(能夠經過SO_KEEPALIVE選項設置。)可是這樣作其實並不被推薦。實際上這套機制只是操做系統底層使用的一個被動機制,原理上不該該被上層應用層使用。當系統關閉一個由KEEPALIVE機制檢查出來的死鏈接時,是不會主動通知上層應用的,只有在調用相應的IO操做在返回值中檢查出來。
在《UNIX網絡編程第1卷》中也有詳細的闡述:
SO_KEEPALIVE 保持鏈接檢測對方主機是否崩潰,避免(服務器)永遠阻塞於TCP鏈接的輸入。設置該選項後,若是2小時內在此套接口的任一方向都沒有數據交換,TCP就自動給對方 發一個保持存活探測分節(keepalive probe)。這是一個對方必須響應的TCP分節.它會致使如下三種狀況:對方接收一切正常:以指望的ACK響應。2小時後,TCP將發出另外一個探測分節。對方已崩潰且已從新啓動:以RST響應。套接口的待處理錯誤被置爲ECONNRESET,套接口自己則被關閉。對方無任何響應:源自berkeley的TCP發送另外8個探測分節,相隔75秒一個,試圖獲得一個響應。在發出第一個探測分節11分鐘 15秒後若仍無響應就放棄。套接口的待處理錯誤被置爲ETIMEOUT,套接口自己則被關閉。如ICMP錯誤是「host unreachable(主機不可達)」,說明對方主機並無崩潰,可是不可達,這種狀況下待處理錯誤被置爲 EHOSTUNREACH。
所以,忘記SO_KEEPALIVE,在應用層本身寫一套保活機制比較靠譜。
Socket中操做該屬性的方法以下:
boolean getKeepAlive() 測試是否啓用 SO_KEEPALIVE。 void setKeepAlive(boolean on) 啓用/禁用 SO_KEEPALIVE。 |
是否重用處於TIME_WAIT狀態的地址。默認爲false。
Socket中操做該屬性的方法以下:
boolean getReuseAddress() 測試是否啓用 SO_REUSEADDR。 void setReuseAddress(boolean on) 啓用/禁用 SO_REUSEADDR 套接字選項。 |