Android 中的Socket通訊
公司新專案中涉及到Socket通訊有關的東西,雖然之前接觸到一點,不過好長時間不用基本上忘個七七八八了,網上查了查資料,根據專案中的需求自己做了個小Demo,歡迎大家指正.
1.需求:
1.1:客戶端測量完畢後,將測量資料以Socket的方式上傳的服務端;
1.2:客戶端每過1秒中向服務端傳送一次心跳,服務端記錄心跳時間並且每兩秒鐘判斷一次心跳間隔,如果上一次心跳時間與現在時間間隔超過兩秒鐘視為連線異常,否則為連線正常.
2.基本概念
2.1:TCP IP協議:在早期計算機網路中,各大廠商都有著自己的一套網路協議,互不相容,這就導致說中文的只能跟說中文的交流而不能跟說英文的交流,這樣的現象很不友好,為了讓不同種類的所有計算機都能毫無障礙的交流,就必須要使用一套統一的標準,所以internet應運而生,在Internet的各種協議中,最重要的兩個協議就是TCP和IP協議.
2.2:在數以萬計的計算機中,如果你想和某臺計算機A通訊,那麼就必須知道A這臺計算機在網際網路中的ip地址(ip地址是每個計算機在網路中的唯一標識),而ip協議就是負責把你要傳送的資料分割成n段打包然後從你的計算機發送到A計算機,但是在運輸的過程中ip協議不會保證能把資料完整的送到A計算機,當然也不能保證資料會按照切割的先後順序到達A計算機.
2.3:TCP協議是建立在IP協議之上的協議,TCP協議會在兩臺計算機之間建立可靠的連線,並且會保證ip協議切割打包的ip包完整順序的到達A計算機,即使在傳輸的過程中丟包了,TCP協議也會重新發送ip包,保證資料的完整性.TCP協議會把傳輸的資料流分成適當長度的報文段,一個單獨的報文段除了包含要傳輸的資料流外還包含源計算機ip地址、埠和目標計算機ip地址、埠.
2.4:埠:在給A計算機發訊息的時候,只知道A的IP地址是不夠的,還需要知道給A計算機的哪個程式發訊息,例如我是給A計算機的微信傳送的資料,那麼我就需要知道微信這個網路程式向計算機申請的埠號,這樣兩個計算機中的兩個網路程式才能互相建立連線.
2.5:TCP/IP的四層協議:
2.5.1網路介面層(資料鏈路層)是物理傳輸通道,對於網路層下發的ip資料包通過網路選擇合適的路徑轉發;對於從網路上接收到的物理幀,抽出ip資料包並交給網路層.
2.5.2網路層對於網路介面層上傳的ip資料包進行資料檢驗,檢驗此資料是否到達目的地址,是則去除包頭將剩餘資料上傳到傳輸層,否則選擇合適的路徑繼續轉發;對於傳輸層下發的分組資料新增包頭,封裝成ip資料包交給網路介面層.
ARP:address resolution protocol 地址解析協議,通過獲取到的ip地址來尋找獲取相應主機的MAC地址;
RARP: reverse address resolution protocol 反地址解析協議,通過已知的MAC地址來獲取相應主機的ip地址;
ICMP:Internet control manage protocol 網路控制管理協議,是網路層的補充,用於實現報文回送功能,像PING命令就是一種ICMP協議,用於傳送ICMP的echo包,用於檢驗網路是否通暢。
2.5.3傳輸層實現兩臺主機的應用程式之間的端到端的通訊,傳輸層可以格式化資訊流、提供可靠傳輸(為實現可靠傳輸,傳輸層協議規定接收端必須發回確認,假如分組丟失,必須重新發送).
2.5.4應用層是應用程式間溝通的層,這裡對使用者來說是一個個的圖形化介面,如瀏覽器 QQ 微信等,為發起網路請求、接收網路傳輸提供具體的應用程式.
3.Socket
3.1 Socket(套接字)對TCP/IP協議進行封裝,是一個供我們程式設計呼叫的介面,它屬於傳輸層,所以Socket分為流套接字(streamsocket,基於TCP協議)和資料報套接字(datagramsocket,基於UDP協議),
TCP協議:提供的是面向連線、可靠的位元組流服務,客戶端和伺服器進行資料互動前雙方必須建立連線,之後才能傳輸資料,TCP提供超時重發,丟棄重複資料,檢驗資料,流量控制等功能,保證資料能從一端傳到另一端。
UDP協議:是一個簡單的面向資料報的運輸層協議,他只是負責把應用程式傳給ip層的資料報發出去(不會像TCP那樣拆分),但是不能保證他們能到達目的地,由於UDP在傳輸資料的時候不用建立連線、沒有超時重發等機制,故UDP的傳輸速度很快.
3.2 Socket使用(基於TCP)
<!-- 允許應用程式改變網路狀態 --> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <!-- 允許應用程式改變WIFI連線狀態 --> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- 允許應用程式訪問有關的網路資訊 --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 允許應用程式訪問WIFI網絡卡的網路資訊 --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 允許應用程式完全使用網路 --> <uses-permission android:name="android.permission.INTERNET" />//客戶端
Socket mSocket = new Socket("192.168.55.13", 20000); mSocket.setSoTimeout(10000);//設定連線超時時間 mSocket.setTcpNoDelay(true);//設定立即傳送 DataOutputStream dataOutputStream = new DataOutputStream(mSocket.getOutputStream());//獲取輸出流 DataInputStream dataInputStream = new DataInputStream(mSocket.getInputStream());//獲取輸入流//讀取服務端傳送過來的訊息
class SocketThrad extends Thread { @Override public void run() { while (true) { if (dataInputStream != null) { try { final String msg = dataInputStream.readUTF(); runOnUiThread(new Runnable() { @Override public void run() { mText.setText(msg); } }); } catch (IOException e) { try { dataOutputStream.close(); dataInputStream.close(); } catch (IOException e1) { e1.printStackTrace(); } e.printStackTrace(); } } } } }//給服務端傳送訊息
@Override public void onClick(View v) { if (dataOutputStream != null) { try { dataOutputStream.writeUTF("訊息"); dataOutputStream.flush(); } catch (IOException e) { try { dataOutputStream.close(); dataInputStream.close(); } catch (IOException e1) { e1.printStackTrace(); } e.printStackTrace(); } } //服務端程式碼class SocketThread extends Thread { @Override public void run() { try { mServerSocket = new ServerSocket(20000); while (true) { Log.d("TAG", "等待SOCKET連線"); mSocket = mServerSocket.accept(); dataOutputStream = new DataOutputStream(mSocket.getOutputStream()); dataInputStream = new DataInputStream(mSocket.getInputStream()); Log.d("TAG", "SOCKET連線"); readMsg(dataInputStream); } } catch (IOException e) { try { dataOutputStream.close(); dataInputStream.close(); } catch (IOException e1) { e1.printStackTrace(); } e.printStackTrace(); } } }private void readMsg(DataInputStream dataInputStream) { while (true) { if (dataInputStream != null) { try { Log.d("TAG", "等待資料傳送"); final String msg = dataInputStream.readUTF(); runOnUiThread(new Runnable() { @Override public void run() { Log.d("TAG", "有資料傳送"); mText.setText(msg); } }); } catch (IOException e) { try { dataOutputStream.close(); dataInputStream.close(); } catch (IOException e1) { e1.printStackTrace(); } e.printStackTrace(); } } } }//給客戶端傳送訊息
@Override public void onClick(View v) { if (dataOutputStream != null) { try { dataOutputStream.writeUTF("訊息"); dataOutputStream.flush(); } catch (IOException e1) { try { dataOutputStream.close(); dataInputStream.close(); } catch (IOException e) { e.printStackTrace(); } e1.printStackTrace(); } } }
目前只是實現了兩個單機之間的相互簡單通訊,貼上了部分程式碼以供大家參考,有什麼建議歡迎指出;