安卓开发学习之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的要简洁一些

猜你喜欢

转载自blog.csdn.net/qq_37475168/article/details/80625663