Java中TCP通信的实现之双向通信2

404 阅读3分钟

之前的文章(Java中TCP通信的实现: juejin.cn/post/720183… )在研究 TCP 通信的双向通信时,客户端和服务端必须交替请求,才能保持正常会话。

现在就来对这个问题进行解决吧。

1 思路

解决思路是使用多线程的方式:

一个线程用于发送消息;

一个线程用于接收消息。

其中,发送消息的线程,具有的特点是:接收键盘输入内容,并将内容通过 Socket 对象发送到客户端或服务端;

接收消息的线程,特点是:接收从客户端/服务端发来的请求。

2 服务端

2.1 创建接收线程

接收线程,用于接收处理客户端请求。

当收到客户端发来结束请求的 “再见”时,设置 ReceiveBufferSize 值为1,并跳出循环。

//接收消息
class Receive extends Thread{
    private Socket socket;

    Receive(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        this.receiveMessage();
    }

    //接收消息的方法
    private void receiveMessage(){
        try{
            BufferedReader bw = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            while (true){
                String line = bw.readLine();
                System.out.println("接收到客户端请求 " + line);

                if("再见".equals(line)){
                    socket.setReceiveBufferSize(1);
                    break;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

2.2 创建发送线程

发送线程,用于服务端向客户端发送消息。

在发送线程里,需要接收键盘输入,以便和客户端进行互动。

当判断 ReceiveBufferSize 的值是否为1,如果设置为了1,结束当前会话并退出循环。

//发送消息
class Send extends Thread{
    private Socket socket;

    public Send(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        this.sendMessage();
    }

    //发送消息的方法
    private void sendMessage(){
        try{
            //创建向客户端发送消息的输出流
            PrintWriter pw = new PrintWriter(socket.getOutputStream());
            //创建接收键盘输入对象
            Scanner scanner = new Scanner(System.in);

            while (true) {
                if(socket.getReceiveBufferSize() == 1){
                    socket.close();
                    break;
                }
                String output = scanner.nextLine();
                pw.println(output);
                pw.flush();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

2.3 创建服务端主线程

在服务端主线程中创建 ServerSocket 对象及接收客户端连接的 Socket 对象,并分别启动接收消息、发送消息线程。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class Server {
    public static void main(String[] args) {
        try{
            ServerSocket serverSocket = new ServerSocket(10001);
            System.out.println("服务端启动,等待客户端连接……");

            while (true) {
                Socket socket = serverSocket.accept();

                new Send(socket).start();
                new Receive(socket).start();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

3 客户端

3.1 创建接收线程

当收到服务端发来的结束请求“再见”时,关闭 Socket 连接并退出循环。

//接收消息
class ClientReceive extends Thread{
    private Socket socket;

    ClientReceive(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        this.receiveMessage();
    }

    //接收消息的方法
    private void receiveMessage(){
        try{
            BufferedReader bw = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            while (true){
                String line = bw.readLine();
                System.out.println("接收到服务端信息 " + line);

                //当收到客户端发来结束请求时,关闭会话
                if("再见".equals(line)){
                    socket.close();
                    break;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

3.2 创建发送线程

当客户端发出“再见”时,即退出循环。

//发送消息
class ClientSend extends Thread{
    private Socket socket;

    public ClientSend(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        this.sendMessage();
    }

    //发送消息的方法
    private void sendMessage(){
        try{
            //创建向客户端发送消息的输出流
            PrintWriter pw = new PrintWriter(socket.getOutputStream());
            //创建接收键盘输入对象
            Scanner scanner = new Scanner(System.in);

            while (true) {
                String output = scanner.nextLine();
                pw.println(output);
                pw.flush();

                if("再见".equals(output)){
                    break;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

3.3 创建客户端主线程

在客户端主线程中分别启动接收消息、发送消息线程。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args) {
        try{
            Socket socket = new Socket("localhost", 10001);

            new ClientSend(socket).start();
            new ClientReceive(socket).start();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

以上就实现了客户端与服务端的互动通信,且不受接收顺序的限制。

4 启动服务端和客户端并相互发送消息

客户端和服务端可以发送多轮消息,并在相互发出“再见”时,结束了客户端的会话。

image.png

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 25 天,点击查看活动详情