Android IPC通信之Socket

  Unix系統中支持進程間通信(IPC),IPC的接口設計得類似文件IO操作接口。在Unix中,一個進程會有一套可以進行讀取寫入的IO描述符。IO描述符可以說是文件、設備或者是通信通道(socket套接字)。一個文件描述符由三部分組成:創建(打開socket)、讀取寫入數據(接收和發送到socket)、銷燬(關閉socket)。
  消息的目的地址是使用socket地址來表示,一個socket地址是由網絡地址和端口號組成的通信標識符。
  進程間通信操作需要客戶端和服務器端分別有一個socket對象。當一個消息發出後,這個消息在發送端的socket中處於排隊狀態,直到下層的網絡協議將這些消息發送出去;當消息到達接收端的socket後,也會處於排隊狀態,直到接收端的進程對這條消息進行了接收處理。
  對於socket編程可以有兩種通信協議可以選擇:數據報通信和流通信。數據報通信就是UDP,UDP是一種無連接的協議,這意味着我們每次發送數據報時,需要同時發送本機的socket描述符和接收端的描述符。流通信即TCP,TCP是一種基於連接的協議,在通信之前,必須在通信的一對兒socket之間建立連接,其中的一個socket作爲服務器進行監聽請求,另一個作爲客戶端進行連接請求,一旦兩個socket建立好了連接,他們可以單向或者雙向進行數據傳輸。
  TCP還是UDP的選擇。
  UDP報頭有64KB的限制,發送的數據不一定會被接收端按順序接收;而TCP一旦socket建立了連接,它們之間的通信如同IO流,沒有大小限制,接收端收到的包和發送端的順序一致。因此TCP適合遠程登錄和文件傳輸這類的網絡服務,因爲這些需要傳輸的數據大小不確定。UDP相比TCP輕量一些,用於實現實時性較高或者丟包不重要的一些服務,例如微信消息的接收發送,在局域網內,UDP的丟包率相對較低。

一、基於UDP的Socket通信

客戶端

客戶端通過將數據放到byte數組中:

byte[] Data = “你想要發送的文本”.getBytes();

並通過一個數據包對象將數據封裝:

DatagramPacket packetS  = new DatagramPacket(Data,Data.length,address,port);

最後通過DataGramSocket對象將該數據包發送:

client = new DatagramSocket();
client.send(packetS);

(1)設置網絡連接權限
在manifest文件的application前添加網絡訪問權限:

<uses-permission android:name="android.permission.INTERNET" />

(2)客戶端代碼

package com.ipc.lijiao.udpcilent;

import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

public class MainActivity extends AppCompatActivity {

    EditText et;
    Button sd;
    int port = 8800;
    String host = "127.0.0.1";
    DatagramSocket client;
    Handler mhandler = new Handler();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et = (EditText)findViewById(R.id.et);
        sd = (Button)findViewById(R.id.sd);

        sd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String content = et.getText().toString();

                Send send = new Send(content);
                send.start();
            }
        });
    }

    public  class  Send  extends Thread{
        String content;
        InetAddress address;
        private  Send(String content)
        {
            this.content = content;
        }
        public  void run()
        {
            try {
                 address = InetAddress.getByName(host);
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
            byte[] Data = content.getBytes();
            DatagramPacket packetS  = new DatagramPacket(Data,Data.length,address,port);
            try {
                client = new DatagramSocket();
            } catch (SocketException e) {
                e.printStackTrace();
            }
            try {
                client.send(packetS);
            } catch (IOException e) {
                e.printStackTrace();
            }
            byte[] DataSend = new byte[300];
            /**
            DatagramPacket 需要新建對象,創建方式如果爲packetS = new DatagramPacket(DataSend,DataSend.length);packetS仍然會指向原對象。
            **/
            DatagramPacket packetR = new DatagramPacket(DataSend,DataSend.length);

            try {
                client.receive(packetR);
            } catch (IOException e) {
                e.printStackTrace();
            }
            content = new String(packetR.getData(),0,packetR.getLength());
            mhandler.post(new Runnable() {
                @Override
                public void run() {
                    et.setText(content);
                }
            });
            client.close();
        }

    }

}

服務端

(2)服務器端
服務器端首先需要設置一個數據接收容器:

byte[] Data = new byte[300];

設置接收數據包:

DatagramPacket packetR = new DatagramPacket(Data,Data.length);

通過DataGramSocket對象接收數據到DatagramPacket對象中:

server = new DatagramSocket(8800);
server.receive(packetR);

讀取數據:

content = new String(packetR.getData(),0,packetR.getLength());

(1)設置網絡連接權限
在manifest文件的application前添加網絡訪問權限:

<uses-permission android:name="android.permission.INTERNET" />

(2)服務端代碼

package com.ipc.lijiao.udpserver;

import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class MainActivity extends AppCompatActivity {

    int Max_SIZE = 300;
    TextView show;
    Handler mhandler;
    String content;
    DatagramSocket server;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        show = (TextView)findViewById(R.id.show);
        mhandler = new Handler();
        Server ImpServer = new Server();
        Thread server = new Thread(ImpServer);
        server.start();
    }

    public class  Server implements   Runnable{


        @Override
        public void run() {
            byte[] Data = new byte[Max_SIZE];
            DatagramPacket packetR = new DatagramPacket(Data,Data.length);
            try {
                 server = new DatagramSocket(8800);
            } catch (SocketException e) {
                e.printStackTrace();
            }
            while (true)
            {
                try {
                    server.receive(packetR);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                content = new String(packetR.getData(),0,packetR.getLength());
                mhandler.post(new Runnable() {
                    @Override
                    public void run() {
                        show.setText(content);
                    }
                });
                byte[] DataS = ("Servier:"+content).getBytes();
                DatagramPacket packetSend = new DatagramPacket(DataS,DataS.length,packetR.getAddress(),packetR.getPort());
                try {
                    server.send(packetSend);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }

    public void onDestroy(){
        super.onDestroy();
        server.close();
    }
}

二、基於TCP的Socket通信

客戶端

(1)基本思路
客戶端通過服務端的host和port創建一個Socket對象:

client = new Socket(host,port);

向服務端發送消息使用BufferedWriter對象:

BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
client.getOutputStream()));
writer.write(show.getText().toString());
writer.flush();
client.shutdownOutput();

使用client.shutdownOutput()單向關閉寫入流(如果不關閉寫入流,則數據不會真正寫到服務端),如果使用writer.close()會將socket對象一併關閉。

(2)demon

package com.ipc.lijiao.ipcclient;

import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    EditText show ;
    Button socketB ;
    Handler mhandler = new Handler();
    Socket client;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        show = (EditText)findViewById(R.id.show);
        socketB = (Button)findViewById(R.id.socketB);
        socketB.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch(v.getId())
        {
            case R.id.socketB:
            {
                new Thread()
                {
                    public void run()
                    {
                       // String host = "172.17.100.77";
                        String host = "127.0.0.1";
                        int port = 8919;
                        try
                        {
                                client = new Socket(host,port);
                                BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
                                writer.write(show.getText().toString());
                                writer.flush();
                                client.shutdownOutput();

                                BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
                                String content = "";

                                while(true)
                                {
                                    String line = null;
                                    line = reader.readLine();
                                    if(line == null)
                                        break;
                                    content = content + line;
                                }
                                client.shutdownInput();

                                final String context = content;
                                mhandler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        show.setText(context);
                                    }
                                });
                            client.close();
                        }catch(IOException e)
                        {
                            e.printStackTrace();
                        }
                    }
                }.start();
            }
        }
    }

    public void onDestroy() {
        super.onDestroy();
        mhandler.removeCallbacksAndMessages(null);
        mhandler = null;
    }
}

服務端

(1)基本思路
服務端會創建ServerSocket對象,並通過該對象的accept()方法監聽端口:

ServerSocket server = new ServerSocket(port);
Socket socket = server.accept();

socket對象通過BufferReader來接收信息,並通過ReadLine讀取信息:

BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true)
{
    String line = null;
    line = reader.readLine();
    if(line == null)
        break;
}

(2)Demon

package com.ipc.lijiao.ipcserver;

import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;

import javax.security.auth.Destroyable;

public class MainActivity extends AppCompatActivity {
    TextView show;
    Handler mhandler = new Handler();
    ServerSocket server;
    Socket socket;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        show = (TextView)findViewById(R.id.textView);
        AcceptClient();
    }

    private void AcceptClient()
    {
        new Thread()
        {
            @Override
            public void run()
            {
                    try
                    {
                        int port = 8919;
                        server = new ServerSocket(port);
                        while(true)
                        {
                            socket = server.accept();
                            String content = "";
                            BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
                            while (true)
                            {
                                String line = null;
                                line = reader.readLine();//讀取客戶端傳來的數據
                                if (line == null)
                                    break;
                                content = content + line;
                            }
                            socket.shutdownInput();
                            final String context = content;
                            mhandler.post(new Runnable() {
                                @Override
                                public void run() {
                                    show.setText(context);
                                }
                            });
                            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                            writer.write("客戶端發送到服務端的消息爲:"+context);
                            writer.flush();
                            socket.shutdownOutput();

                        }
                    }
                    catch(IOException e)
                    {
                        e.printStackTrace();
                    }
            }
        }.start();
    }

    public void onDestroy() {
        super.onDestroy();
        try {
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mhandler.removeCallbacksAndMessages(null);
        mhandler = null;
        try {
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            server.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
發佈了22 篇原創文章 · 獲贊 5 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章