1. 程式人生 > >Android 中的Socket通訊

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();
        }
    }
}

目前只是實現了兩個單機之間的相互簡單通訊,貼上了部分程式碼以供大家參考,有什麼建議歡迎指出;