Network programming - TCP stream socket


Column introduction: JavaEE from entry to advanced

Topic source: leetcode, Niuke, Jianzhi offer.

Creation goal: record the learning process of learning JavaEE

I hope to help others while improving myself, and make progress together with everyone and grow up with each other.

Academic qualifications represent the past, ability represents the present, and learning ability represents the future! 


Table of contents

1. Java stream socket programming model

2.SeverSocket API

3.Socket API

4. Long and short connections in TCP

5. Code example:


1. Java stream socket programming model


2.SeverSocket API

SeverSocket is an API to create a TCP server Socket.

The construction method of SeverSocket :

method signature Method Description
ServerSocket(int port) Create a server-side stream socket Socket and bind it to the specified port

ServerSocket method:

method signature Method Description
Socket accept() Start listening to the port (the port bound at the time of creation), and after a client connects, return a server-side Socket object, and establish a connection with the client based on the Socket, otherwise block and wait.
void close() close this socket

3.Socket API

Socket is the client Socket, or the server Socket returned by the server after receiving the request from the client to establish a connection (accept method).

Regardless of whether it is a client-side or a server-side Socket, after the two parties establish a connection, save the peer-end information and use it to send and receive data with the other party.

Socket construction method:

method signature Method Description
Socket(String host,int port) Create a client stream socket Socket, and establish a connection with the corresponding port number on the corresponding IP host.

 Socket method:

method signature Method Description
InetAddress getInetAddress() Returns the address the socket is connected to
InputStream getInputStream() Returns the socket's input stream
InputStream getOutputStream() Returns the input stream for this socket

4. Long and short connections in TCP

When TCP sends data, a connection needs to be established, and when the connection is closed, it is determined whether it is a short connection or a long connection.

  • Short connection : After receiving data and returning a response each time, the connection is closed (short connection can only send and receive data once)
  • Long connection : do not close the connection, keep the connection state all the time, both parties send and receive data continuously (long connection can send and receive data multiple times)

Compared with the above long and short connections, the difference between the two is as follows:

  • Time-consuming to establish and close a connection : For a short connection, each request and response need to establish a connection and close the connection; while a long connection only needs to establish the connection for the first time, and subsequent requests and responses can be transmitted directly. Relatively speaking, the connection It takes time to establish and close , so long connections are more efficient.
  • The active sending request is different : the short connection is generally the client actively sends the request to the server; while the long connection client sends the request actively, or the server actively sends the request.
  • The usage scenarios of the two are different : the short connection is suitable for scenarios with low frequency of client requests, such as browsing the web, etc. The long connection is suitable for scenarios with frequent communication between the client and the server, such as chat rooms, real-time games, etc.

Extended understanding:

Long connections based on BIO (synchronous blocking IO) will always occupy system resources. For service systems with high concurrency requirements, such consumption is unbearable.

Since each connection needs to be continuously blocked waiting to receive data, each connection will run in a thread.

A block corresponds to a request, a response, and non-stop processing is the characteristic of a long connection: the connection is not closed all the time, and the request is processed non-stop.

In practical application: The server is generally based on NIO (that is, synchronous non-blocking IO) to achieve long connections, and the performance can be greatly improved. ( IO multiplexing )


5. Code example:

server:

public class TcpEchoSever {
    private ServerSocket serverSocket = null;

    public TcpEchoSever(int port) throws IOException {
        this.serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("启动服务器!");
        while (true) {
            //使用这个 clientSocket 和具体的客户端进行交流.
            Socket clientSocket = serverSocket.accept();
            processConnection(clientSocket);
        }
    }

    //使用这个方法来处理一个连接
    //这一个连接对应到一个客户端 , 但这里可能会涉及到多次交互
    private void processConnection(Socket clientSocket) {
        System.out.printf("[%s %d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
        // 基于上述 socket 对象和客户端机通信
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {
            //由于要处理多个请求响应 , 也需要使用循环来进行
            while (true) {
                //1.读取请求
                Scanner scan = new Scanner(inputStream);
                if (!scan.hasNext()) {
                    //没有下个数据 , 说明读完了(客户端关闭了连接)
                    System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
                    break;
                }
                //此处使用的 next 返回结果中不包含空白符.
                String request = scan.next();
                //2.根据请求计算响应
                String response = process(request);
                //3.返回响应
                PrintWriter printWriter = new PrintWriter(outputStream);
                //此处使用 println 来写入 , 让结果带有一个 \n 来换行 , 方便对端接收解析
                printWriter.println(response);
                //flush 用来刷新缓冲区 , 保证当前写入的数据确实发出去了.
                printWriter.flush();

                System.out.printf("[%s:%d] req: %s; resp %s \n", clientSocket.getInetAddress().toString(), clientSocket.getPort(),
                        request, response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                clientSocket.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private String process(String request) {
        return request;
    }


    public static void main(String[] args) throws IOException {
        TcpEchoSever server = new TcpEchoSever(9090);
        server.start();
    }
}

client:

public class TcpEchoClient {
    private Socket socket = null;

    public TcpEchoClient(String serverIp , int serverPort) throws IOException {
        // Socket 构造方法 , 能够识别点分十进制格式的 IP 地址 , 比 DatagramPacket 更方便
        // new 这个对象的同时就会和服务器进行 TCP 连接操作.
        socket = new Socket(serverIp , serverPort);
    }

    public void start(){
        System.out.println("客户端启动!");
        Scanner scanner = new Scanner(System.in);
        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()) {
            while(true){
                //1. 先从键盘上读取到用户输入的内容
                System.out.println(">");
                String request = scanner.next();
                if(request.equals("exit")){
                    System.out.println("goodbye");
                    break;
                }
                //2. 把读到的内容构造成请求发送给服务器
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(request);
                printWriter.flush();
                //3. 读取服务器的响应
                Scanner respScanner = new Scanner(inputStream);
                String response = respScanner.next();
                //4. 把响应显示到界面上
                System.out.println(response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1" , 9090);
        tcpEchoClient.start();
    }
}

It can be seen from this code that every time the server connects to a client, it will block and wait for the client to send a request. If the client does not actively disconnect, the thread will be occupied. Therefore, in the case of a single thread, a server can only connect a client.

 public void start() throws IOException {
        System.out.println("启动服务器!");
        while (true) {
            //使用这个 clientSocket 和具体的客户端进行交流.
            Socket clientSocket = serverSocket.accept();
            processConnection(clientSocket);
        }
    }

 In order to meet the needs of connecting multiple clients, we need to create multiple threads, but creating/destroying threads continuously requires a lot of overhead, so we can consider using a thread pool.

public void start() throws IOException {
        System.out.println("启动服务器!");
        ExecutorService threadPool = Executors.newCachedThreadPool();
        while(true){
            //使用这个 clientSocket 和具体的客户端进行交流.
            Socket clientSocket = serverSocket.accept();
            //多线程版本
//            Thread t = new Thread(()->{
//                processConnection(clientSocket);
//            });
//            t.start();
            //线程池版本
            threadPool.submit(()->{
                processConnection(clientSocket);
            });
        }
    }

operation result:

 

 

How to open multiple clients?

 

 

Guess you like

Origin blog.csdn.net/liu_xuixui/article/details/128798163