接上一篇博文,在小小添加幾個Socket選項,
html
4.SO_LINGER選項:java
//設置該選項: public void setSoLinger(boolean on,int seconds) throws SocketException // 注意第二個參數以秒爲單位,並不是毫秒 //讀取該選項: public int getSoLinger() throws SocketException
SO_LINGER選項用來控制Socket關閉時的行爲,默認狀況下,當執行Socket的close()方法,該方法會當即返回,但底層的Socket實際上並不當即關閉,它會延遲一段時間知道發送完全部剩餘的數據,纔會真正關閉Socket,才斷開鏈接。
數組
但對於如下狀況: 服務器
socket.setSoLinger(true,0); //該方法也會當即返回,但底層的Socket也會當即關閉,全部未發送完的剩餘數據被丟棄。 //還在網絡上傳輸,可是未被接收方接收的數據, socket.setSoLinger(true,1000); // 而該方法不會當即返回,而是進入阻塞狀態。 只有當底層的Soket發送完全部的剩餘數據或阻塞時間已經超過了1000秒,也回返回,可是剩餘未發送的數據被丟棄。
//對於該選項,嘗試着讓服務器端先睡眠一會,再開始接受數據:
網絡
//服務器端程序: public class SimpleServer { public static void main(String[] args) throws Exception { ServerSocket server = new ServerSocket(7777); Socket socket = server.accept(); System.out.println("服務器困了,因而決定休息會..."); Thread.sleep( 1000*10 ); // 睡眠10秒。 System.out.println("服務器終於睡醒了,而後開始接受數據:"); InputStream in = socket.getInputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); byte[] buff = new byte[1024]; int length = -1; while( ( length = in.read(buff) ) != -1){ buffer.write(buff,0,length); } System.out.println( new String( buffer.toByteArray() )); //把字節數組轉換成字符串。 } } //客戶端程序: public class SimpleClient { public static void main(String[] args) throws Exception { Socket socket = new Socket("localhost",7777); //socket.setSoLinger(true, 0); //(1) 該條件下,會強行關閉底層的Socket,致使全部未發送數據丟失,而服務器算接受方睡醒後接受數據,因爲底層Socket關閉,則會拋出SocketException: Connection reset. //socket.setSoLinger(true, 300); //(2) 注意Socket關閉時間,實際上會等待一段時間在關閉 OutputStream out = socket.getOutputStream(); StringBuffer sb = new StringBuffer(); String str = "關於對setSoLinger選項的設置:"; sb.append( str ); for( int i=1;i<=10000;i++){ // 需注意jvm堆棧的設置。 sb.append( i ); } System.out.println( sb ); out.write( sb.toString().getBytes() ); System.out.println("開始關閉Socket: "); long start = System.currentTimeMillis(); socket.close(); // 注意不一樣條件下(1)與(2)的關閉的時間 long end = System.currentTimeMillis(); System.out.println("關閉Socket所用時間爲: " + (end - start ) +"ms" ); } }
5.SO_RCVBUF選項: 該選項用於 輸入數據 的緩衝區的大小。app
//設置該選項: public void setReceiveBufferSize(int size) throws SocketException //讀取該選項: public int getReceiveBufferSize() throws SocketException
6.SO_SNDBUF選項:該選項用於 輸出數據的緩衝區的大小。jvm
//設置該選項: public void setSendBufferSize(int size) throws SocketException; //讀取該選項: public void getSendBufferSize() throws SocketException;
public class SocketBufferDemo { public static void main(String[] args) throws SocketException { Socket socket = new Socket();//不帶參的構造方法,不會試圖創建與服務器端的鏈接. //默認狀況下的輸入輸出緩衝區的大小: int rcvbuf = socket.getReceiveBufferSize(); // 輸入緩衝區大小 int sndbuf = socket.getSendBufferSize(); //輸出緩衝區大小 System.out.println( rcvbuf + "\t" + sndbuf ); //從新設置輸入輸出緩衝區的大小再輸出結果: socket.setReceiveBufferSize( 1024*32 ); socket.setSendBufferSize( 1024*32 ); System.out.println( socket.getReceiveBufferSize()+"\t"+socket.getSendBufferSize() ); } }
7.SO_KEEPALIVE選項: socket
//設置該選項: public void setKeepAlive(boolean on) throws SocketException //讀取該選項: public int getKeepAlive() throws SocketException
當該選項爲true時,表示底層的TCP實現會監視該鏈接是否有效。當鏈接處於空閒狀態(即鏈接的兩端沒有互相傳送數據)超過2H,本地的TCP實現會發送一個數據包給遠程的Socket。若是遠程Socket沒有發回響應,TCP實現就會持續嘗試11分鐘,直到接收到響應爲止。若是在12分鐘內未收到響應,TCP實現就會自動關閉本地Socket,斷開鏈接。(在不一樣的網絡平臺上,TCP實現嘗試與遠程Socket對話的時限會有所差異)。tcp
該選項默認值爲 false,即表示TCP不會監視鏈接是否有效,不活動的客戶端可能會永久存在下去,而不會注意到服務器已經崩潰。spa
對於KeepAlive這種系統底層的機制(用於系統維護每個tcp鏈接的),可在認識到另外一種新的概念,即心跳線程:
連接網址:①; http://code.taobao.org/p/tfs/wiki/dataserver/background/ <心跳線程簡單介紹>
②: http://blog.csdn.net/xuyuefei1988/article/details/8279812 《關於心跳包機制》
③:http://blog.sina.com.cn/s/blog_616e189f0100s3px.html <這篇也不錯,Socket緩衝區探討>
8.OOBINLINE選項:
//設置該選項: public void setOOBInline(boolean on ) throws SocketException //讀取該選項: public void getOOBInline() throws SocketException public void sendUrgentData(int data) throws IOException //雖然sendUrgentData的參數data是int類型,但只有這個int類型的低字節被髮送(即一個字節,8位),其它的三個字節被忽略。
該選項爲true時,表示支持發送一個字節的TCP緊急數據。Socket類的sendUrgentData( int data ) 方法用於發送一個字節(8位)的TCP緊急數據。
而爲false時,表示當接收方收到緊急數據時不做任何處理,直接丟棄。
可是問題是接收方會把接收到的緊急數據與普通數據放在一樣的隊列中,除非使用一些更高層次的協議,不然接收方處理緊急數據的能力很是有限,當緊急數據到來時,接收方不會獲得任何的通知,所以接收方很難區分普通數據與緊急數據,只好按一樣的方式處理他們。
//服務端代碼: public class OOBInlineServer { public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(7777); System.out.println("服務器已經啓動,端口號:7777"); while (true) { Socket socket = serverSocket.accept(); // 須在服務端中也設置爲true,不然沒法接受到客戶端發送過來的緊急數據: socket.setOOBInline(true); InputStream in = socket.getInputStream(); InputStreamReader inReader = new InputStreamReader(in); BufferedReader bReader = new BufferedReader(inReader); System.out.println(bReader.readLine()); System.out.println(bReader.readLine()); socket.close(); } } } //客戶端代碼: public class OOBInlineClient { public static void main(String[] args) throws Exception { Socket socket = new Socket("localhost", 7777); socket.setOOBInline(true); OutputStream out = socket.getOutputStream(); OutputStreamWriter outWriter = new OutputStreamWriter(out); outWriter.write(67); // 向服務器發送字符"C" outWriter.write(" Hello World\r\n "); socket.sendUrgentData(65); // "A" socket.sendUrgentData(68); // "D" outWriter.flush(); socket.sendUrgentData(322); // "B" 322分佈在兩個字節上,可是其低位爲:0100 0010 即恰好跟 66 同樣, socket.close(); } }
猜想下上述輸出結果: 猜想可能爲:
C Hello World
A D B
但正常結果輸出爲:
因而可知:使用sendUrgentData()方法發送數據後,系統會當即將這些數據發送出去,而使用write()(首先是將數據存放在了緩衝區)發送數據,必需要使用flush()方法纔會真正發送數據。
另外注意的是:在使用 setOOBInline()方法時,要注意必須在客戶端和服務端程序同時使用該方法,打開SO_OOBInline選項,不然沒法命名sendUrgentData來發送數據。