Android Socket 發送與接收資料問題: 發送後的資料接收到總是粘包

來源:互聯網
上載者:User

標籤:

先說明一下粘包的概念: 發送時是兩個單獨的包、兩次發送,但接收時兩個包連在一起被一次接收到。
在以前 WinCE 下 Socket 編程,確實也要處理粘包的問題,沒想到在 Android 下也遇到了。
首先想從發送端能否避免這樣的問題,例如: (1) 調用強制刷資料完成發送的函數;(2) 設定發送逾時。
1 先試了調用 flush() 函數,但運行後現象依舊
2 設定發送逾時是 Windows 平台的做法,但在 Android 平台下是否有類似的設定呢?
查看 Socket 類的實現代碼:java.net.socket socket.class 檔案後發現,還是有函數可以完成這樣的設定的。請看如下函數和變數的說明:

1 /** 2  * Sets this socket‘s {@link SocketOptions#TCP_NODELAY} option. 3  */  4 public void setTcpNoDelay(boolean on) throws SocketException {  5     checkOpenAndCreate(true);  6     impl.setOption(SocketOptions.TCP_NODELAY, Boolean.valueOf(on));  7 }  

1 /** 2  * This boolean option specifies whether data is sent immediately on this socket. 3  * As a side-effect this could lead to low packet efficiency. The 4  * socket implementation uses the Nagle‘s algorithm to try to reach a higher 5  * packet efficiency if this option is disabled. 6  */  7 public static final int TCP_NODELAY = 1;  

一般情況下,只需要調用如下的代碼即可:

1 public Socket clientSocket = null;  2 // 執行個體化對象並串連到伺服器  3 clientSocket = new Socket("172.25.103.1",12589);  

不用做任何設定就可以完成與伺服器/用戶端的通訊,剛開始我也是這樣做的。所以,遇到了上面的問題。

1 // 發送資料包,預設為 false,即用戶端發送資料採用 Nagle 演算法;  2 // 但是對於即時互動性高的程式,建議其改為 true,即關閉 Nagle 演算法,用戶端每發送一次資料,無論資料包大小都會將這些資料發送出去  3 clientSocket.setTcpNoDelay(true);  

看以下較完整的 Socket 初始化與設定過程:

 1 /* * * * * * * * * * 用戶端 Socket 通過構造方法串連伺服器 * * * * * * * * * */   2 try {   3   // 用戶端 Socket 可以通過指定 IP 位址或網域名稱兩種方式來串連伺服器端,實際最終都是通過 IP 位址來串連伺服器   4   // 建立一個Socket,指定其IP地址及連接埠號碼   5   Socket clientSocket = new Socket("172.25.103.1",12589);   6   // 用戶端socket在接收資料時,有兩種逾時:1. 串連伺服器逾時,即連線逾時;2. 串連伺服器成功後,接收伺服器資料逾時,即接收逾時   7   // 設定 socket 讀取資料流的逾時時間   8   clientSocket.setSoTimeout(5000);   9   // 發送資料包,預設為 false,即用戶端發送資料採用 Nagle 演算法;  10   // 但是對於即時互動性高的程式,建議其改為 true,即關閉 Nagle 演算法,用戶端每發送一次資料,無論資料包大小都會將這些資料發送出去  11   clientSocket.setTcpNoDelay(true);  12   // 設定用戶端 socket 關閉時,close() 方法起作用時延遲 30 秒關閉,如果 30 秒內盡量將未發送的資料包發送出去  13   clientSocket.setSoLinger(true, 30);  14   // 設定輸出資料流的發送緩衝區大小,預設是4KB,即4096位元組  15   clientSocket.setSendBufferSize(4096);  16   // 設定輸入資料流的接收緩衝區大小,預設是4KB,即4096位元組  17   clientSocket.setReceiveBufferSize(4096);  18   // 作用:每隔一段時間檢查伺服器是否處於活動狀態,如果伺服器端長時間沒響應,自動關閉用戶端socket  19   // 防止伺服器端無效時,用戶端長時間處於串連狀態  20   clientSocket.setKeepAlive(true);  21   // 用戶端向伺服器端發送資料,擷取用戶端向伺服器端輸出資料流  22   OutputStream osSend = clientSocket.getOutputStream();  23   OutputStreamWriter osWrite = new OutputStreamWriter(osSend);  24   BufferedWriter bufWrite = new BufferedWriter(osWrite);  25   // 代表可以立即向伺服器端發送單位元組資料  26   clientSocket.setOOBInline(true);  27   // 資料不經過輸出緩衝區,立即發送  28   clientSocket.sendUrgentData(0x44);//"D"  29   // 向伺服器端寫資料,寫入一個緩衝區  30   // 註:此處字串最後必須包含“\r\n\r\n”,告訴伺服器HTTP頭已經結束,可以處理資料,否則會造成下面的讀取資料出現阻塞  31   // 在write() 方法中可以定義規則,與後台匹配來識別相應的功能,例如登入Login() 方法,可以寫為write("Login|LeoZheng,0603 \r\n\r\n"),供後台識別;  32   bufWrite.write("Login|LeoZheng,0603 \r\n\r\n");  33   // 發送緩衝區中資料 - 前面說調用 flush() 無效,可能是調用的方法不對吧!  34   bufWrite.flush();  35 }  36 catch (UnknownHostException e) {  37     e.printStackTrace();  38 } catch (IOException e) {  39     e.printStackTrace();  40 }  
 1 /* * * * * * * * * * Socket 用戶端讀取伺服器端響應資料 * * * * * * * * * */   2 try {   3   // serverSocket.isConnected 代表是否串連成功過   4   // 判斷 Socket 是否處於串連狀態   5   if(true == serverSocket.isConnected() && false == serverSocket.isClosed()) {   6     // 用戶端接收伺服器端的響應,讀取伺服器端向用戶端的輸入資料流   7     InputStream isRead = serverSocket.getInputStream();   8     // 緩衝區   9     byte[] buffer = new byte[isRead.available()];  10     // 讀取緩衝區  11     isRead.read(buffer);  12     // 轉換為字串  13     String responseInfo = new String(buffer);  14     // 日誌中輸出  15     Log.i("Socket Server", responseInfo);  16   }  17   // 關閉網路  18   serverSocket.close();  19 }  20 catch (UnknownHostException e) {  21     e.printStackTrace();  22 } catch (IOException e) {  23     e.printStackTrace();  24 }  
 1 /* * * * * * * * * * Socket 用戶端通過 connect 方法串連伺服器 * * * * * * * * * */   2 try {   3   Socket serverSocket = new Socket();   4   // 使用預設的連線逾時   5   serverSocket.connect(new InetSocketAddress("172.25.103.1",12589));        // 連線逾時 3 秒: serverSocket.connect(new InetSocketAddress("172.25.103.1",12589),3000);   6      7   // 關閉 socket   8   serverSocket.close();   9 }  10 catch (UnknownHostException e) {  11     e.printStackTrace();  12 } catch (IOException e) {  13     e.printStackTrace();  14 }  

最後說明一點: 無論 Socket 如何設定,接收方是一定要處理粘包的問題的。即在接收時,對接收到的資料進行分析,看是否存在資料不全或粘包的現象。

Android Socket 發送與接收資料問題: 發送後的資料接收到總是粘包

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.