1. 程式人生 > >安卓開發學習之TCP通訊

安卓開發學習之TCP通訊

背景

這幾天在學習安卓的程序間通訊,而socket也可以實現這一功能,以可靠連線協議TCP為例,實現一個服務端和客戶端的應答應用

功能:客戶端發訊息,服務端把訊息原樣返回,如果收到小寫over,退出

步驟

1、服務端,在Service的onCreate()裡面,新建一個執行緒建立服務端的socket,並實現客戶端的應答

public class PeopleService extends Service {

    private boolean isOut = false;
    private boolean isOver = false;

    @Override
    public void onCreate() {

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    final ServerSocket serverSocket = new ServerSocket(12345);
                    System.out.println("等待客戶端連線");

                    while (!isOut) { // 服務端不退出,這個執行緒一直執行
                        final Socket client = serverSocket.accept(); // 阻塞在此,直到客戶端連線
                        System.out.println("客戶端已連線,ip地址:" + client.getInetAddress().getHostAddress() + "埠號:" + client.getLocalPort());

                        // 客戶端連線後,等待1s以便客戶端發訊息
                        Thread.sleep(1000);
                        try {
                            BufferedInputStream inputStream = new BufferedInputStream(client.getInputStream());
                            BufferedOutputStream outputStream = new BufferedOutputStream(client.getOutputStream());

                            System.out.println("開始和客戶端通訊");
                            while (!isOver) { // 訊息不為over,執行緒一直執行
                                while (inputStream.available() <= 0); // 過濾空訊息

                                byte[] bytes = new byte[1024];
                                int len;
                                StringBuffer stringBuffer = new StringBuffer();

                                while (inputStream.available() > 0 && (len = inputStream.read(bytes)) != -1) {
                                    stringBuffer.append(new String(bytes, 0, len)); 
                                } // 下面就不對stringBuffer的內容判空了,因為是空的話,根本跳不出上面的過濾

                                String fromClient = stringBuffer.toString();

                                System.out.println("客戶端資訊:" + fromClient);

                                outputStream.write(fromClient.getBytes());
                                outputStream.flush();

                                isOver = fromClient.equals("over");

                                isOut = isOver;
                            }

                            System.out.println("over..");
                            inputStream.close();
                            outputStream.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }


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

    @Override
    public IBinder onBind(Intent intent) {
        logger("有連線請求");
        logger(intent.toString());
        return null;
    }
}

整個過程在一個新執行緒的原因是service裡不讓進行耗時操作,所以才新建執行緒

在利用BufferedOutputStream讀訊息時,一定先要利用available()過濾空訊息。

這和檔案IO不一樣,檔案IO的話輸入流的長度是固定的,所以只用read(byte[])就可以讀到輸入流的末尾,但如果是通訊的話,似乎客戶端不關閉socket,輸入流就一直不到頭,所以得用available()得到可讀位元組數,從而進行訊息過濾

2、客戶端

佈局的話,就是上面一個EditText輸入訊息,下面一個Button傳送,訊息,佈局檔案略過

連線伺服器、讀取伺服器訊息放在onStart()裡面,同樣要新建一個執行緒

    @Override
    protected void onStart() {
        super.onStart();
        if (!isConnected) {
            final Intent intent = new Intent(this, PeopleService.class);
            intent.setAction("com.example.songzeceng");
            bindService(intent, connection, BIND_AUTO_CREATE);

            new Thread(new Runnable() {
                @Override
                public void run() {
                    // 等待1s,以便服務端建立socket
                    try {
                        Thread.sleep(1000);
                        server = new Socket("localhost", 12345);

                        inputStream = new BufferedInputStream(server.getInputStream());
                        while (!isOver) { // 只要不結束,這個執行緒一直執行
                            while (inputStream.available() <= 0); // 過濾空訊息

                            byte[] bytes = new byte[1024];
                            int len;
                            StringBuffer stringBuffer = new StringBuffer();
                            while (inputStream.available() > 0 && (len = inputStream.read(bytes)) != -1) {
                                stringBuffer.append(new String(bytes, 0, len));
                            }
                            String fromServer = stringBuffer.toString();
                            System.out.println(fromServer);
                            isOver = fromServer.equals("over");
                        }
                        System.out.println("over..");
                        inputStream.close();
                        outputStream.close();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

在傳送按鈕的監聽裡面,傳送資訊,當然也是一個新執行緒,在onCreate()裡面

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        et_input = (EditText) findViewById(R.id.client_input);
        findViewById(R.id.client_send).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            outputStream = new BufferedOutputStream(server.getOutputStream());

                            String toServer = et_input.getText().toString();

                            outputStream.write(toServer.getBytes());
                            outputStream.flush();

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

            }
        });
    }

執行結果

服務端


客戶端


結語

Socket通訊應該是安卓IPC裡僅有的不用Binder的方式了(忽略共享檔案),而java的TCP/UDP比C的要簡潔一些