Socket 通信原理(Android客戶端和服務器以TCP&&UDP方式互通)


ZERO、前言

有關通信原理內容是在網上或百科整理得到,代碼部分為本人所寫,如果不當,還望指教。

一、Socket通信簡介 

Android與服務器的通信方式主要有兩種,一是Http通信,一是Socket通信。兩者的最大差異在於,http連接使用的是“請求—響應方式”,即在請求時建立連接通道,當客戶端向服務器發送請求后,服務器端才能向客戶端返回數據。而Socket通信則是在雙方建立起連接后就可以直接進行數據的傳輸,在連接時可實現信息的主動推送,而不需要每次由客戶端想服務器發送請求。 那么,什么是socket?Socket又稱套接字,在程序內部提供了與外界通信的端口,即端口通信。通過建立socket連接,可為通信雙方的數據傳輸傳提供通道。socket的主要特點有數據丟失率低,使用簡單且易於移植。

1.1什么是Socket Socket
是一種抽象層,應用程序通過它來發送和接收數據,使用Socket可以將應用程序添加到網絡中,與處於同一網絡中的其他應用程序進行通信。簡單來說,Socket提供了程序內部與外界通信的端口並為通信雙方的提供了數據傳輸通道。

 1.2Socket的分類
 根據不同的的底層協議,Socket的實現是多樣化的。本指南中只介紹TCP/IP協議族的內容,在這個協議族當中主要的Socket類型為流套接字(streamsocket)和數據報套接字(datagramsocket)。流套接字將TCP作為其端對端協議,提供了一個可信賴的字節流服務。數據報套接字使用UDP協議,提供數據打包發送服務。 下面,我們來認識一下這兩種Socket類型的基本實現模型。

二、Socket 基本通信模型

 

三、Socket基本實現原理

 3.1基於TCP協議的Socket 
服務器端首先聲明一個ServerSocket對象並且指定端口號,然后調用Serversocket的accept()方法接收客戶端的數據。accept()方法在沒有數據進行接收的處於堵塞狀態。(Socketsocket=serversocket.accept()),一旦接收到數據,通過inputstream讀取接收的數據。
  客戶端創建一個Socket對象,指定服務器端的ip地址和端口號(Socketsocket=newSocket("172.168.10.108",8080);),通過inputstream讀取數據,獲取服務器發出的數據(OutputStreamoutputstream=socket.getOutputStream()),最后將要發送的數據寫入到outputstream即可進行TCP協議的socket數據傳輸。
3.2基於UDP協議的數據傳輸 
服務器端首先創建一個DatagramSocket對象,並且指點監聽的端口。接下來創建一個空的DatagramSocket對象用於接收數據(bytedata[]=newbyte[1024;]DatagramSocketpacket=newDatagramSocket(data,data.length)),使用DatagramSocket的receive方法接收客戶端發送的數據,receive()與serversocket的accepet()類似,在沒有數據進行接收的處於堵塞狀態。
客戶端也創建個DatagramSocket對象,並且指點監聽的端口。接下來創建一個InetAddress對象,這個對象類似與一個網絡的發送地址(InetAddressserveraddress=InetAddress.getByName("172.168.1.120")).定義要發送的一個字符串,創建一個DatagramPacket對象,並制定要講這個數據報包發送到網絡的那個地址以及端口號,最后使用DatagramSocket的對象的send()發送數據。*(Stringstr="hello";bytedata[]=str.getByte();DatagramPacketpacket=new DatagramPacket(data,data.length,serveraddress,4567);socket.send(packet);)

四、android 實現socket簡單通信

前言:添加權限

<!--允許應用程序改變網絡狀態-->  
<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"/>  

4.1使用TCP協議通信

android端實現:

    protected void connectServerWithTCPSocket() {

        Socket socket;
        try {// 創建一個Socket對象,並指定服務端的IP及端口號
            socket = new Socket("192.168.1.32", 1989);
            // 創建一個InputStream用戶讀取要發送的文件。
            InputStream inputStream = new FileInputStream("e://a.txt");
            // 獲取Socket的OutputStream對象用於發送數據。
            OutputStream outputStream = socket.getOutputStream();
            // 創建一個byte類型的buffer字節數組,用於存放讀取的本地文件
            byte buffer[] = new byte[4 * 1024];
            int temp = 0;
            // 循環讀取文件
            while ((temp = inputStream.read(buffer)) != -1) {
                // 把數據寫入到OuputStream對象中
                outputStream.write(buffer, 0, temp);
            }
            // 發送讀取的數據到服務端
            outputStream.flush();

            /** 或創建一個報文,使用BufferedWriter寫入,看你的需求 **/
//            String socketData = "[2143213;21343fjks;213]";
//            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
//                    socket.getOutputStream()));
//            writer.write(socketData.replace("\n", " ") + "\n");
//            writer.flush();
            /************************************************/
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

服務器端簡單實現:

    public void ServerReceviedByTcp() {
        // 聲明一個ServerSocket對象
        ServerSocket serverSocket = null;
        try {
            // 創建一個ServerSocket對象,並讓這個Socket在1989端口監聽
            serverSocket = new ServerSocket(1989);
            // 調用ServerSocket的accept()方法,接受客戶端所發送的請求,
            // 如果客戶端沒有發送數據,那么該線程就停滯不繼續
            Socket socket = serverSocket.accept();
            // 從Socket當中得到InputStream對象
            InputStream inputStream = socket.getInputStream();
            byte buffer[] = new byte[1024 * 4];
            int temp = 0;
            // 從InputStream當中讀取客戶端所發送的數據
            while ((temp = inputStream.read(buffer)) != -1) {
                System.out.println(new String(buffer, 0, temp));
            }
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

4.2使用UDP協議通信

客戶端發送數據實現:

    protected void connectServerWithUDPSocket() {
        
        DatagramSocket socket;
        try {
            //創建DatagramSocket對象並指定一個端口號,注意,如果客戶端需要接收服務器的返回數據,
            //還需要使用這個端口號來receive,所以一定要記住
            socket = new DatagramSocket(1985);
            //使用InetAddress(Inet4Address).getByName把IP地址轉換為網絡地址  
            InetAddress serverAddress = InetAddress.getByName("192.168.1.32");
            //Inet4Address serverAddress = (Inet4Address) Inet4Address.getByName("192.168.1.32");  
            String str = "[2143213;21343fjks;213]";//設置要發送的報文  
            byte data[] = str.getBytes();//把字符串str字符串轉換為字節數組  
            //創建一個DatagramPacket對象,用於發送數據。  
            //參數一:要發送的數據  參數二:數據的長度  參數三:服務端的網絡地址  參數四:服務器端端口號 
            DatagramPacket packet = new DatagramPacket(data, data.length ,serverAddress ,10025);  
            socket.send(packet);//把數據發送到服務端。  
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }  
    }

客戶端接收服務器返回的數據:

    public void ReceiveServerSocketData() {
        DatagramSocket socket;
        try {
            //實例化的端口號要和發送時的socket一致,否則收不到data
            socket = new DatagramSocket(1985);
            byte data[] = new byte[4 * 1024];
            //參數一:要接受的data 參數二:data的長度
            DatagramPacket packet = new DatagramPacket(data, data.length);
            socket.receive(packet);
            //把接收到的data轉換為String字符串
            String result = new String(packet.getData(), packet.getOffset(),
                    packet.getLength());
            socket.close();//不使用了記得要關閉
            System.out.println("the number of reveived Socket is  :" + flag
                    + "udpData:" + result);
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

服務器接收客戶端實現:

    public void ServerReceviedByUdp(){
        //創建一個DatagramSocket對象,並指定監聽端口。(UDP使用DatagramSocket)  
        DatagramSocket socket;
        try {
            socket = new DatagramSocket(10025);
            //創建一個byte類型的數組,用於存放接收到得數據  
            byte data[] = new byte[4*1024];  
            //創建一個DatagramPacket對象,並指定DatagramPacket對象的大小  
            DatagramPacket packet = new DatagramPacket(data,data.length);  
            //讀取接收到得數據  
            socket.receive(packet);  
            //把客戶端發送的數據轉換為字符串。  
            //使用三個參數的String方法。參數一:數據包 參數二:起始位置 參數三:數據包長  
            String result = new String(packet.getData(),packet.getOffset() ,packet.getLength());  
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }  
    }

五、總結:

使用UDP方式android端和服務器端接收可以看出,其實android端和服務器端的發送和接收大庭相徑,只要端口號正確了,相互通信就沒有問題,TCP使用的是流的方式發送,UDP是以包的形式發送。

demo地址:http://download.csdn.NET/detail/mad1989/5626975


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM