Java Socket技術總結

1       Socket通訊原理

1.1     ISO七層模型

1.2     TCP/IP五層模型

 

         應用層至關於OSI中的會話層,表示層,應用層。html

         區別參考:http://blog.chinaunix.net/uid-22166872-id-3716751.htmljava

 

1.3     TCP報文

 

(1)序號:Seq序號,佔32位,用來標識從TCP源端向目的端發送的字節流,發起方發送數據時對此進行標記。算法

  (2)確認序號:Ack序號,佔32位,只有ACK標誌位爲1時,確認序號字段纔有效,Ack=Seq+1。設計模式

  (3)標誌位:共6個,即URG、ACK、PSH、RST、SYN、FIN等,具體含義以下:瀏覽器

  (A)URG:緊急指針(urgent pointer)有效。服務器

  (B)ACK:確認序號有效。網絡

  (C)PSH:接收方應該儘快將這個報文交給應用層。多線程

  (D)RST:重置鏈接。架構

  (E)SYN:發起一個新鏈接。socket

  (F)FIN:釋放一個鏈接。

 

 須要注意的是:

  (A)不要將確認序號Ack與標誌位中的ACK搞混了。

  (B)確認方Ack=發起方Req+1,兩端配對。

1.4     Socket通訊

       Socket是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP /IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有,讓Socket去組織數據,以符合指定的協議。

1.5     三次握手

         Socket鏈接創建和關閉,詳見:http://www.2cto.com/net/201310/251896.html

2       通訊基本概念

2.1     短鏈接

鏈接->傳輸數據->關閉鏈接 

短鏈接是指SOCKET鏈接,發送數據,接收數據後,立刻斷開鏈接。 

好比:

HTTP1.0默認是短鏈接,無狀態的,瀏覽器和服務器每進行一次HTTP操做,就創建一次鏈接,但任務結束就中斷鏈接。 、

Http1.1默認是長鏈接

無狀態:協議對於事務處理沒有記憶能力;

2.2     長鏈接

鏈接->傳輸數據->保持鏈接 -> 傳輸數據-> 。。。 ->關閉鏈接。 

創建SOCKET鏈接後,不論是否使用,一致保持鏈接。

2.3     半包

接受方沒有接受到一個完整的包,只接受了部分;

緣由:TCP爲提升傳輸效率,將一個包分配的足夠大,致使接受方並不能一次接受完。

影響:長鏈接和短鏈接中都會出現

2.4     粘包

發送方發送的多個包數據到接收方接收時粘成一個包,從接收緩衝區看,後一包數據的頭緊接着前一包數據的尾。

分類:一種是粘在一塊兒的包都是完整的數據包,另外一種狀況是粘在一塊兒的包有不完整的包

出現粘包現象的緣由是多方面的:

1)發送方粘包:由TCP協議自己形成的,TCP爲提升傳輸效率,發送方每每要收集到足夠多的數據後才發送一包數據。若連續幾回發送的數據都不多,一般TCP會根據優化算法把這些數據合成一包後一次發送出去,這樣接收方就收到了粘包數據。

2)接收方粘包:接收方用戶進程不及時接收數據,從而致使粘包現象。這是由於接收方先把收到的數據放在系統接收緩衝區,用戶進程從該緩衝區取數據, 若下一包數據到達時前一包數據還沒有被用戶進程取走,則下一包數據放到系統接收緩衝區時就接到前一包數據以後,而用戶進程根據預先設定的緩衝區大小從系統接 收緩衝區取數據,這樣就一次取到了多包數據。

2.5     分包

分包(1):在出現粘包的時候,咱們的接收方要進行分包處理;

分包(2):一個數據包被分紅了屢次接收;

緣由:1. IP分片傳輸致使的;2.傳輸過程當中丟失部分包致使出現的半包;3.一個包可能被分紅了兩次傳輸,在取數據的時候,先取到了一部分(還可能與接收的緩衝區大小有關係)。

影響:粘包和分包在長鏈接中都會出現

2.6     如何解決半包,粘包問題

出現粘包和半包現象,是由於TCP當中,只有流的概念,沒有包的概念。

UDP不會出現半包,粘包狀況,緣由是UDP是一個完整的數據包,發送時不進行合併,所以接收的時候就不存在粘包狀況。

 

固定長度:每次發送固定長度的數據;

特殊標示:以回車,換行做爲特殊標示;獲取到指定的標識時,說明包獲取完整。

字節長度:包頭+包長+包體的協議形式,當服務器端獲取到指定的包長時才說明獲取完整;

 

參考文章(有圖):http://blog.csdn.net/pi9nc/article/details/17165171

2.7     何時須要考慮粘包的狀況

短鏈接:不用考慮粘包的狀況;

發送數據無結構,如文件傳輸,這樣發送方只管發送,接收方只管接收存儲,也不用考慮粘包;

長鏈接:須要在鏈接後一段時間內發送不一樣結構數據;

 

處理方式:接收方建立一預處理線程,對接收到的數據包進行預處理,將粘連的包分開;

2.8     TCP與UDP的差異

  • 基於鏈接與無鏈接;
  • 對系統資源的要求(TCP較多,UDP少);
  • UDP程序結構較簡單;
  • 流模式與數據報模式 ;
  • TCP保證數據正確性,UDP可能丟包,TCP保證數據順序,UDP不保證。
  • 網絡分化:機器自己是好的,可是之間的通訊出現問題;
  • 網絡抖動:網絡中的延遲是指信息從發送到接收通過的延遲時間,通常由傳輸延遲及處理延遲組成;而抖動是指最大延遲與最小延遲的時間差,如最大延遲是20毫秒,最小延遲爲5毫秒,那麼網絡抖動就是15毫秒,它主要標識一個網絡的穩定性。

2.9     其餘通訊問題

2.10         參考資料

一個包沒有固定長度,以太網限制在46-1500字節,1500就是以太網的MTU,超過這個量,TCP會爲IP數據報設置偏移量進行分片傳輸,如今通常可容許應用層設置8k(NTFS系)的緩衝區,8k的數據由底層分片,而應用看來只是一次發送。

 

對於UDP,就不要太大,通常在1024至10K。注意一點,你不管發多大的包,IP層和鏈路層都會把你的包進行分片發 送,通常局域網就是1500左右,廣域網就只有幾十字節。分片後的包將通過不一樣的路由到達接收方,對於UDP而言,要是其中一個分片丟失,那麼接收方的 IP層將把整個發送包丟棄,這就造成丟包。

 

TCP做爲流,發包是不會整包到達的,而是源源不斷的到,那接收方就必須組包。而UDP做爲消息或數據報,它必定是整包到達接收方。

 

關於接收,通常的發包都有包邊界,首要的就是你這個包的長度要讓接收方知道,因而就有個包頭信息,對於TCP,接收方先收這個包頭信息,而後再收包 數據。一次收齊整個包也能夠,可要對結果是否收齊進行驗證。這也就完成了組包過程。UDP,那你只能整包接收了。要是你提供的接收Buffer過 小,TCP將返回實際接收的長度,餘下的還能夠收,而UDP不一樣的是,餘下的數據被丟棄並返回WSAEMSGSIZE錯誤。注意TCP,要是你提供的 Buffer佷大,那麼可能收到的就是多個發包,你必須分離它們,還有就是當Buffer過小,而一次收不完Socket內部的數據,那麼Socket接 收事件(OnReceive),可能不會再觸發,使用事件方式進行接收時。

3       Socket實例

3.1     Socket通訊模型

 

 

 

3.2     Socket架構完整模型

  • 解決半包,粘包(編解碼)
  • 服務端支持多線程
  • 支持心跳檢測
  • 指定端口實例化一個SeverSocket
  • 調用ServerSocket的accept()方法,以在等待鏈接期間形成阻塞
  • 獲取位於該底層的Socket的流以進行讀寫操做
  • 將數據封裝成流
  • 對Socket進行讀寫
  • 關閉打開的流

3.3     服務端開發

 

//創建ServerSocket對象,監聽綁定端口

ServerSocket server=new ServerSocket(1000);

 

//創建接收Socket,阻塞響應

Socket client=server.accept();

 

//得到輸入流,用於接收客戶端信息

InputStream in=server.getInputStream();

//得到輸出流,用於輸出客戶端信息

 

//對輸入流進行包裝,方便使用

BufferedReader inRead=new BufferedReader(new InputStreamReader(in));

//對輸出流進行包裝,方便使用

PrintWriter outWriter=new PrintWriter(out);

 

while(true)

{

String str=inRead.readLine();//讀入

outWriter.println("輸出到客戶端");

outWriter.flush();//輸出

if(str.equals("end"))

{

break;

}

}

//關閉Socket

client.close();

server.close();

 

3.4     客戶端開發

  • 經過IP地址和端口實例化Socket,請求鏈接服務器
  • 得到Socket上的流以進行讀寫
  • 把流封裝進BufferedReader/PrintWriter的實例
  • 對Socket進行讀寫
  • 關閉打開的流

 

//使用Socket,創建與服務端的鏈接

Socket client=new Socket("127.0.0.1",1000);

InputStream in=client.getInputStream();//得到輸入流

OutputStream out=client.getOutputStream();//得到輸出流

//包裝輸入流,輸出流

BufferedReader inRead=new BufferedReader(new InputStreamReader(in));

PrintWriter outWriter=new PrintWriter(out);

//得到控制檯輸入

BufferedReader inConsole=new BufferedReader(new InputStreamReader(in));

while(true)

{

String str=inConsole.readLine();//讀取控制檯輸入

outWriter.println(str);//輸出到服務端

outWriter.flush();//刷新緩衝區

if(str.equals("end"))

{

break;

}//退出

System.out.println(inRead.readLine())//讀取服務端輸出

}

client.close();

 

DOS下運行客戶端

java -classpath sockettest-0.0.1-SNAPSHOT.jar cn.com.gome.sockettest.basic.ClientTest

3.5     多線程服務端

 

3.6     心跳檢測

         方法1:socket.sendUrgentData(0);//發送1個字節的緊急數據,默認狀況下,服務器端沒有開啓緊急數據處理,不影響正常通訊

try{
      socket.sendUrgentData(0xFF);
}catch(Exception ex){
      reconnect();
}

         只要對方Socket的SO_OOBINLINE屬性沒有打開,就會自動捨棄這個字節,而SO_OOBINLINE屬性默認狀況下就是關閉的。

 

         方法2:自定義心跳字符串

3.7     各種數據讀寫

         傳輸字節,字符,對象【ObjectInputStream ObjectOutputStream】(實例)

相關文章
相關標籤/搜索