歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java Socket 的參數選項解讀

Java Socket 的參數選項解讀

日期:2017/3/1 9:31:12   编辑:Linux編程

Java Socket中有很多參數可以選擇,這篇文章的目的是沉澱出這些參數的語義和用法,供自己以後查閱。

1、java socket參數選項總覽

在JDK1.6中有如下參數選項:

public final static int TCP_NODELAY = 0x0001;
public final static int SO_BINDADDR = 0x000F;
public final static int SO_REUSEADDR = 0x04;
public final static int SO_BROADCAST = 0x0020;
public final static int IP_MULTICAST_IF = 0x10;
public final static int IP_MULTICAST_IF2 = 0x1f;
public final static int IP_MULTICAST_LOOP = 0x12;
public final static int IP_TOS = 0x3;
public final static int SO_LINGER = 0x0080;
public final static int SO_TIMEOUT = 0x1006;
public final static int SO_SNDBUF = 0x1001;
public final static int SO_RCVBUF = 0x1002;
public final static int SO_KEEPALIVE = 0x0008;
public final static int SO_OOBINLINE = 0x1003;

2、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,則是打開這個算法,會對發送的數據進行緩存。

3、public final static int SO_BINDADDR = 0x000F;
獲取綁定套接字的本地地址(不能僅將此選項“設置”為“得到”,因為套接字是在創建時綁定的,所以本地綁定的地址不可更改)。

4、public final static int SO_REUSEADDR = 0x04;

這個參數表示套接字對端口是否可重用。
這個套接字選項通知內核,如果端口忙,但TCP狀態位於 TIME_WAIT ,可以重用端口。如果端口忙,而TCP狀態位於其他狀態,重用端口時依舊得到一個錯誤信息,指明"地址已經使用中"。如果你的服務程序停止後想立即重啟,而新套接字依舊使用同一端口,此時 SO_REUSEADDR 選項非常有用。必須意識到,此時任何非期望數據到達,都可能導致服務程序反應混亂,不過這只是一種可能,事實上很不可能。

一個套接字由相關五元組構成,協議、本地地址、本地端口、遠程地址、遠程端口。SO_REUSEADDR 僅僅表示可以重用本地本地地址、本地端口,整個相關五元組還是唯一確定的。所以,重啟後的服務程序有可能收到非期望數據。必須慎重使用 SO_REUSEADDR 選項。

5、public final static int SO_BROADCAST = 0x0020;

這個參數選項用來控制廣播,目前只有在DatagramSocket裡支持。

6、public final static int IP_MULTICAST_IF = 0x10;

用來控制多播的參數選項,目前只有在MulticastSocket裡支持


在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;
}
}

7、public final static int IP_MULTICAST_IF2 = 0x1f;

這個字段的效果和上面的是一樣的,只是擴展支持IPV6

8、public final static int IP_MULTICAST_LOOP = 0x12;

用來設置本地回環接口的多播特性,在MulticastSocket源代碼中有相關方法:

/**
* 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));
}

9、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在一般情況下用不到。

10、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。

11、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,那麼就一直阻塞,否則阻塞直到超時後直接拋超時異常。

12、public final static int SO_SNDBUF = 0x1001;

在默認情況下,輸出流的發送緩沖區是8096個字節(8K)。這個值是Java所建議的輸出緩沖區的大小。如果這個默認值不能滿足要求,可以用setSendBufferSize方法來重新設置緩沖區的大小。

13、public final static int SO_RCVBUF = 0x1002;

在默認情況下,輸入流的接收緩沖區是8096個字節(8K)。這個值是Java所建議的輸入緩沖區的大小。如果這個默認值不能滿足要求,可以用setReceiveBufferSize方法來重新設置緩沖區的大小。

14、public final static int SO_KEEPALIVE = 0x0008;

如果將這個參數這是為True,客戶端每隔一段時間(一般不少於2小時)就像服務器發送一個試探性的數據包,服務器一般會有三種回應:

1、服務器正常回一個ACK,這表明遠程服務器一切OK,那麼客戶端不會關閉連接,而是再下一個2小時後再發個試探包。

2、服務器返回一個RST,這表明遠程服務器掛了,這時候客戶端會關閉連接。

3、如果服務器未響應這個數據包,在大約11分鐘後,客戶端Socket再發送一個數據包,如果在12分鐘內,服務器還沒響應,那麼客戶端Socket將關閉。

15、public final static int SO_OOBINLINE = 0x1003;

如果這個Socket選項打開,可以通過Socket類的sendUrgentData方法向服務器發送一個單字節的數據。這個單字節數據並不經過輸出緩沖區,而是立即發出。雖然在客戶端並不是使用OutputStream向服務器發送數據,但在服務端程序中這個單字節的數據是和其它的普通數據混在一起的。因此,在服務端程序中並不知道由客戶端發過來的數據是由OutputStream還是由sendUrgentData發過來的。

Copyright © Linux教程網 All Rights Reserved