java socket中有不少參數能夠選擇,這篇博客的目的是沉澱出這些參數的語義和用法,供本身之後查閱。java
一、java socket參數選項總覽算法
在JDK1.6中有以下參數選項:緩存
1 public final static int TCP_NODELAY = 0x0001; 2 3 public final static int SO_BINDADDR = 0x000F; 4 5 public final static int SO_REUSEADDR = 0x04; 6 7 public final static int SO_BROADCAST = 0x0020; 8 9 public final static int IP_MULTICAST_IF = 0x10; 10 11 public final static int IP_MULTICAST_IF2 = 0x1f; 12 13 public final static int IP_MULTICAST_LOOP = 0x12; 14 15 public final static int IP_TOS = 0x3; 16 17 public final static int SO_LINGER = 0x0080; 18 19 public final static int SO_TIMEOUT = 0x1006; 20 21 public final static int SO_SNDBUF = 0x1001; 22 23 public final static int SO_RCVBUF = 0x1002; 24 25 public final static int SO_KEEPALIVE = 0x0008; 26 27 public final static int SO_OOBINLINE = 0x1003;
二、public final static int TCP_NODELAY = 0x0001;服務器
要理解這個參數,首先要理解Nagle算法,下面先說說這個Nagle算法
2.1 Nagle算法產生的背景
當網絡傳輸中存在大量小包傳輸時,會嚴重影響傳輸效率。好比一個包,包頭40字節,而真正的內容只有一個字節或者幾個字節(典型的有Telnet),這樣的傳輸效率是十分低下的。Nagle算法要解決的就是這種低效率的傳輸問題。
2.2 Nagle算法的原理
用通俗的話來講就是,把小包要發送的字節先緩存,當到達必定的閥值的時候再一次性傳輸。具體算法(僞代碼)以下:
if there is new data to send if the window size >= MSS and available data is >= MSS send complete MSS segment now else if there is unconfirmed data still in the pipe enqueue data in the buffer until an acknowledge is received else send data immediately end if end if end if
其中MSS爲maximum segment size的縮寫,是TCP頭部的一個字段,表示一個TCP段最大的數據承載量。
2.3 Nagle算法的問題
在傳輸大文件的時候,若是使用這個算法,那麼會出現明顯的延遲現象,所以,在這種狀況下,最好是關閉這個算法。
知道了Nagle算法,就知道了TCP_NODELAY這個參數的意義了,若是這個參數被設置爲True,那麼就是關閉Nagle算法,實現無延遲傳輸,若是設置爲false,則是打開這個算法,會對發送的數據進行緩存。
三、public final static int SO_BINDADDR = 0x000F;
獲取綁定套接字的本地地址(不能僅將此選項「設置」爲「獲得」,由於套接字是在建立時綁定的,因此本地綁定的地址不可更改)。
四、public final static int SO_REUSEADDR = 0x04;
這個參數表示套接字對端口是否可重用。
這個套接字選項通知內核,若是端口忙,但TCP狀態位於 TIME_WAIT ,能夠重用端口。若是端口忙,而TCP狀態位於其餘狀態,重用端口時依舊獲得一個錯誤信息,指明"地址已經使用中"。若是你的服務程序中止後想當即重啓,而新套接字依舊使用同一端口,此時 SO_REUSEADDR 選項很是有用。必須意識到,此時任何非指望數據到達,均可能致使服務程序反應混亂,不過這只是一種可能,事實上很不可能。
在MulticastSocket的源代碼裏有設置多播的方法:網絡
public void setInterface(InetAddress inf) throws SocketException { if (isClosed()) { throw new SocketException("Socket is closed"); } checkAddress(inf, "setInterface"); synchronized (infLock) { getImpl().setOption(SocketOptions.IP_MULTICAST_IF, inf); infAddress = inf; } }
七、public final static int IP_MULTICAST_IF2 = 0x1f;app
這個字段的效果和上面的是同樣的,只是擴展支持IPV6socket
八、public final static int IP_MULTICAST_LOOP = 0x12;ide
用來設置本地迴環接口的多播特性,在MulticastSocket源代碼中有相關方法:oop
/** * Disable/Enable local loopback of multicast datagrams * The option is used by the platform's networking code as a hint * for setting whether multicast data will be looped back to * the local socket. * * <p>Because this option is a hint, applications that want to * verify what loopback mode is set to should call * {@link #getLoopbackMode()} * @param disable <code>true</code> to disable the LoopbackMode * @throws SocketException if an error occurs while setting the value * @since 1.4 * @see #getLoopbackMode */ public void setLoopbackMode(boolean disable) throws SocketException { getImpl().setOption(SocketOptions.IP_MULTICAST_LOOP, Boolean.valueOf(disable)); }
九、public final static int IP_TOS = 0x3;優化
這個參數是用來控制IP頭中的TOS字段的,是用來控制和優化IP包的路徑的,在Socket源代碼裏有一個設置的方法:
/** * Sets traffic class or type-of-service octet in the IP * header for packets sent from this Socket. * As the underlying network implementation may ignore this * value applications should consider it a hint. * * <P> The tc <B>must</B> be in the range <code> 0 <= tc <= * 255</code> or an IllegalArgumentException will be thrown. * <p>Notes: * <p> For Internet Protocol v4 the value consists of an octet * with precedence and TOS fields as detailed in RFC 1349. The * TOS field is bitset created by bitwise-or'ing values such * the following :- * <p> * <UL> * <LI><CODE>IPTOS_LOWCOST (0x02)</CODE></LI> * <LI><CODE>IPTOS_RELIABILITY (0x04)</CODE></LI> * <LI><CODE>IPTOS_THROUGHPUT (0x08)</CODE></LI> * <LI><CODE>IPTOS_LOWDELAY (0x10)</CODE></LI> * </UL> * The last low order bit is always ignored as this * corresponds to the MBZ (must be zero) bit. * <p> * Setting bits in the precedence field may result in a * SocketException indicating that the operation is not * permitted. * <p> * As RFC 1122 section 4.2.4.2 indicates, a compliant TCP * implementation should, but is not required to, let application * change the TOS field during the lifetime of a connection. * So whether the type-of-service field can be changed after the * TCP connection has been established depends on the implementation * in the underlying platform. Applications should not assume that * they can change the TOS field after the connection. * <p> * For Internet Protocol v6 <code>tc</code> is the value that * would be placed into the sin6_flowinfo field of the IP header. * * @param tc an <code>int</code> value for the bitset. * @throws SocketException if there is an error setting the * traffic class or type-of-service * @since 1.4 * @see #getTrafficClass */ public void setTrafficClass(int tc) throws SocketException { if (tc < 0 || tc > 255) throw new IllegalArgumentException("tc is not in range 0 -- 255"); if (isClosed()) throw new SocketException("Socket is closed"); getImpl().setOption(SocketOptions.IP_TOS, new Integer(tc)); }
從源代碼的註釋看,TOS設置了是否生效,和底層的操做系統的實現有關。應用程序沒法保證TOS的變動會對socket鏈接產生影響。我的認爲,TOS在通常狀況下用不到。
十、public final static int SO_LINGER = 0x0080;
先看Socket源代碼:
/** * Enable/disable SO_LINGER with the specified linger time in seconds. * The maximum timeout value is platform specific. * * The setting only affects socket close. * * @param on whether or not to linger on. * @param linger how long to linger for, if on is true. * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. * @exception IllegalArgumentException if the linger value is negative. * @since JDK1.1 * @see #getSoLinger() */ public void setSoLinger(boolean on, int linger) throws SocketException { if (isClosed()) throw new SocketException("Socket is closed"); if (!on) { getImpl().setOption(SocketOptions.SO_LINGER, new Boolean(on)); } else { if (linger < 0) { throw new IllegalArgumentException("invalid value for SO_LINGER"); } if (linger > 65535) linger = 65535; getImpl().setOption(SocketOptions.SO_LINGER, new Integer(linger)); } }
這個字段對Socket的close方法產生影響,當這個字段設置爲false時,close會當即執行並返回,若是這時仍然有未被送出的數據包,那麼這些數據包將被丟棄。若是設置爲True時,有一個延遲時間能夠設置。這個延遲時間就是close真正執行全部等待的時間,最大爲65535。
十一、public final static int SO_TIMEOUT = 0x1006;
/** * Enable/disable SO_TIMEOUT with the specified timeout, in * milliseconds. With this option set to a non-zero timeout, * a read() call on the InputStream associated with this Socket * will block for only this amount of time. If the timeout expires, * a <B>java.net.SocketTimeoutException</B> is raised, though the * Socket is still valid. The option <B>must</B> be enabled * prior to entering the blocking operation to have effect. The * timeout must be > 0. * A timeout of zero is interpreted as an infinite timeout. * @param timeout the specified timeout, in milliseconds. * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. * @since JDK 1.1 * @see #getSoTimeout() */ public synchronized void setSoTimeout(int timeout) throws SocketException { if (isClosed()) throw new SocketException("Socket is closed"); if (timeout < 0) throw new IllegalArgumentException("timeout can't be negative"); getImpl().setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout)); }
這個參數用來控制客戶端讀取socket數據的超時時間,若是timeout設置爲0,那麼就一直阻塞,不然阻塞直到超時後直接拋超時異常。
十二、public final static int SO_SNDBUF = 0x1001;
在默認狀況下,輸出流的發送緩衝區是8096個字節(8K)。這個值是Java所建議的輸出緩衝區的大小。若是這個默認值不能知足要求,能夠用setSendBufferSize方法來從新設置緩衝區的大小。
1三、public final static int SO_RCVBUF = 0x1002;
在默認狀況下,輸入流的接收緩衝區是8096個字節(8K)。這個值是Java所建議的輸入緩衝區的大小。若是這個默認值不能知足要求,能夠用setReceiveBufferSize方法來從新設置緩衝區的大小。
1四、public final static int SO_KEEPALIVE = 0x0008;
若是將這個參數這是爲True,客戶端每隔一段時間(通常很多於2小時)就像服務器發送一個試探性的數據包,服務器通常會有三種迴應:
一、服務器正常回一個ACK,這代表遠程服務器一切OK,那麼客戶端不會關閉鏈接,而是再下一個2小時後再發個試探包。
二、服務器返回一個RST,這代表遠程服務器掛了,這時候客戶端會關閉鏈接。
三、若是服務器未響應這個數據包,在大約11分鐘後,客戶端Socket再發送一個數據包,若是在12分鐘內,服務器還沒響應,那麼客戶端Socket將關閉。
1五、public final static int SO_OOBINLINE = 0x1003;
若是這個Socket選項打開,能夠經過Socket類的sendUrgentData方法向服務器發送一個單字節的數據。這個單字節數據並不通過輸出緩衝區,而是當即發出。雖然在客戶端並非使用OutputStream向服務器發送數據,但在服務端程序中這個單字節的數據是和其它的普通數據混在一塊兒的。所以,在服務端程序中並不知道由客戶端發過來的數據是由OutputStream仍是由sendUrgentData發過來的。