java socket for communication programming [2] - continuous communication and multithreaded communication

The previous section talked about the simple communication between the server and client of java socket, and learned about the communication mechanism of socket. See the following for details: java socket for communication programming

Today, let's go further. There is a problem in the previous example, that is, sending messages only once ends. We know that wechat and QQ send and receive messages continuously. How can we make the client send messages continuously? Now let's discuss it in practice.

1, How does java socket communicate continuously

The server side of socket is blocking communication. It blocks through the accept() method and waits for the connection of the client. After the connection, the client sends messages and sends and receives messages through IO. From the perspective of this process, if we continue to execute this action, we can receive the message from the client. We thought that yes, the server controls the circular sending and receiving of messages through while(true). Let's look at the code of the server:

package socketStudy;

import java.io.*;
import java.net.*;

/**
 * socket Server
 * @author xiaoming
 * @version 1.0
 * @date 2022-01-28
 */
public class CommunicationServer {

    public static String socketserver_ip = "127.0.0.1";
    public static int socketserver_port = 8881;

    public static void main(String[] args) throws IOException {

       startSocketForSimp();

    }

    /**
     * A simple socket server can be connected to a client for continuous communication
     */
    public static void startSocketForSimp(){

        try {
            ServerSocket ss = new ServerSocket(socketserver_port);
            System.out.println("CommunicationServer Start server....Port is:"+socketserver_port+" wait connect...");

            Socket s = ss.accept();
            System.out.println("Client connection received, client:"+s.getInetAddress().getHostAddress()+"Connected to server");

            //Continuously read and send messages
            readAndWriteMsg(s.getInputStream(),s.getOutputStream());

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


    /**
     * Read and write messages
     * @param inp
     * @param outp
     * @throws IOException
     */
    public static void readAndWriteMsg(InputStream inp,OutputStream outp) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(inp));
        //Continuously read the messages sent by the client
        while(true) {
            String mess = br.readLine();
            System.out.println("[[received client information] the information is:" + mess);

            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outp));
            bw.write("[Server] has received the message sent by the client. The message is:"+mess+"\n");
            bw.flush();
        }

    }
}

As can be seen from the above code, when a client connects, it enters the while(true) loop through br Readline() continuously reads messages from the client and prints them to the console. Take another look at the client code:

package moreClientAndThread;

import socketStudy.CommunicationServer;

import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * socket Client code
 * @author xiaoming
 * @version 1.0
 * @date 2022-02-05
 */
public class ClientSocket1 {
        public static void main(String[] args) {
        try {
            //Connect socket server
            Socket s = new Socket(CommunicationServer.socketserver_ip,CommunicationServer.socketserver_port);

            //Build IO
            InputStream inp = s.getInputStream();//Input stream, received information
            OutputStream outp = s.getOutputStream();//Output stream, outgoing message

            //Get the message from the console and send a message to the server
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in,"UTF-8"));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outp));
            while(true){
                String str = bufferedReader.readLine();//Reading messages from the console
                bw.write(str);
                bw.write("\n");//Indicates that a message is over and the server passes
                bw.flush();

                //Read the message returned by the server
                BufferedReader br = new BufferedReader(new InputStreamReader(inp));
                String mess = br.readLine();
                System.out.println("[[received server information]:"+mess);
        }

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

From the client code, we can see that by continuously accepting input messages from the console and sending them to the server, we can see the effect of the following operation:

Server running result:

Client connection received, client:127.0.0.1 Connected to server
[[received client information] the information is: 1-123
[[received client information] the information is: 1-qwea

Client running result:

1-123
[[receive server information]: the [server] has received the message sent by the client. The message is: 1-123
1-qwea
[[receive server information]: the [server] has received the message sent by the client. The message is: 1-qwea

Thus, the client can send messages continuously. But there is also a problem with the above code, because we are blocked there through while, so the new client cannot be connected when connecting, so we can only connect to one client. If we want to realize communication like wechat, we also need to realize multiple clients to send messages at the same time. So how do we realize multi client communication?

2, How does java socket communicate with multiple clients

If we want to realize multiple clients to connect and communicate at the same time, what methods do we have? Let's analyze the processing flow of the server. There are two core points: one is to accept the connection of the client (), and the other is to continuously read your message from the client after connecting. The two places conflict, so we need to separate the two. The separation method is that after receiving a new client connection, start a new thread to process the reading of the client's message and separate it from the main thread, so as to generate multiple threads on the server, and each client is an independent sub thread. To sum up, this method is to process the connection of the client in the main thread of the server and the message reading of the client in the sub thread.

Let's look at the code of the server:

package socketStudy;

import java.io.*;
import java.net.*;

/**
 * socket Server
 * @author xiaoming
 * @version 1.0
 * @date 2022-01-28
 */
public class CommunicationServer {

    public static String socketserver_ip = "127.0.0.1";
    public static int socketserver_port = 8881;

    public static void main(String[] args) throws IOException {

        startSocketForMoreThread();
    }


    /**
     * Multithreaded communication socket server
     */
    public static void startSocketForMoreThread() throws IOException {
        ServerSocket ss = new ServerSocket(socketserver_port);
        System.out.println("CommunicationServer Start server....Port is:"+socketserver_port+" wait connect...");

        while(true){
            Socket s = ss.accept();
            System.out.println("Client connection received, client:"+s.getInetAddress().getHostAddress()+"Connected to server");
            //Start a thread processing
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //Read and write messages
                        readAndWriteMsg(s.getInputStream(),s.getOutputStream());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }
            }).start();
        }
    }

    /**
     * Read and write messages
     * @param inp
     * @param outp
     * @throws IOException
     */
    public static void readAndWriteMsg(InputStream inp,OutputStream outp) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(inp));
        //Continuously read the messages sent by the client
        while(true) {
            Thread t = Thread.currentThread();
            String tname = t.getName();
            String mess = br.readLine();
            System.out.println("thread  name="+tname+"[[received client information] the information is:" + mess);

            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outp));
            bw.write("[Server] has received the message sent by the client. The message is:"+mess+"\n");
            bw.flush();
        }

    }
}

As can be seen from the server code, we separate the read and write of accept() and messages. The read and write of client messages are processed by a single thread. We specially print the name of the thread to distinguish the multi-threaded processing, so as to see the multi-threaded processing process more intuitively. Let's look at the client code again. We wrote two client classes ClientSocket1:

package moreClientAndThread;

import socketStudy.CommunicationServer;

import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * socket Client code
 * @author xiaoming
 * @version 1.0
 * @date 2022-02-05
 */
public class ClientSocket1 {
        public static void main(String[] args) {
        try {
            //Connect socket server
            Socket s = new Socket(CommunicationServer.socketserver_ip,CommunicationServer.socketserver_port);

            //Build IO
            InputStream inp = s.getInputStream();//Input stream, received information
            OutputStream outp = s.getOutputStream();//Output stream, outgoing message

            //Get the message from the console and send a message to the server
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in,"UTF-8"));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outp));
            while(true){
                String str = bufferedReader.readLine();//Reading messages from the console
                bw.write(str);
                bw.write("\n");//Indicates that a message is over and the server passes
                bw.flush();

                //Read the message returned by the server
                BufferedReader br = new BufferedReader(new InputStreamReader(inp));
                String mess = br.readLine();
                System.out.println("[[received server information]:"+mess);
        }

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

Write another class of client 2, ClientSocket2. The code can be the same. I won't post the code of ClientSocket2.

Let's start the server code first, and then start the client code in turn. Let's see the actual operation effect, as follows:

Server running result:

"C:\Program Files\Java\jdk1.8.0_311\bin\java.exe" ...
CommunicationServer Start server....Port is:8881 wait connect...
Client connection received, client:127.0.0.1 Connected to server
 thread  name=Thread-0[[received client information] the information is: 1-123
 thread  name=Thread-0[[message received by client]: 1-qwe
 Client connection received, client:127.0.0.1 Connected to server
 thread  name=Thread-1[[received client information] the information is: 2-qwe
 thread  name=Thread-1[[received client information] the information is: 2-qw1

Running result of client 1:

"C:\Program Files\Java\jdk1.8.0_311\bin\java.exe" ...
1-123
[[receive server information]: the [server] has received the message sent by the client. The message is: 1-123
1-qwe
[[receive server information]: the [server] has received the message sent by the client. The message is: 1-qwe

Running result of client 2:

"C:\Program Files\Java\jdk1.8.0_311\bin\java.exe" ...
2-qwe
[[receive server information]: the [server] has received the message sent by the client. The message is: 2-qwe
2-qw1
[[receive server information]: the [server] has received the message sent by the client. The message is: 2-qw1

From the above, we can see that we have realized the connection and communication of multiple clients.

3, Problems encountered and Solutions

I have encountered some problems in the actual code writing and debugging. Make records and share them.

1. Use of multithreading

We have all learned about the use of multithreading, but we may not have used it in actual projects, and it is easy to forget it after a long time. When I used it, I reviewed the usage of multithreading and found that the knowledge of multithreading is still very deep, including the relationship between packet expansion process and thread, the startup of multithreading, thread pool, inter thread communication, the return of thread processing results, etc, I'll write a separate article to share this follow-up.

I used new Thread(new Runnable() {}) this time. I rewritten the run() method in Runnable. It was normal after running, but I didn't execute the messaging logic in the run() method. After checking, I found that it was not called Because there is no error reported in the start () method, the troubleshooting is a little convoluted. This problem is easy to occur for partners who do not write multithreading for a long time. The correct usage is as follows. Don't forget to call start() method.

new Thread(new Runnable(){
                @Override
                public void run(){
                       //TODO:
                                  }
           }).start()

2. Organize the code into multiple methods and extract common processing logic

In the process of writing code, due to multiple logic switches, the code is kneaded in one method, resulting in each change in one method, which is easy to make mistakes and waste time. Later, the overall process is divided into client connection, message sending and receiving processing, and then called in the main function, so that each change only affects the logic in one method, Thus, the number of errors is greatly reduced, the debugging time is greatly reduced, the processing logic is clearer, and the amount of code is much less.

In addition, extracting some constants for reuse can also reduce the amount of code, reduce the probability of error, and form good habits.

3. Connection reset problem

This problem is quite complex. Many materials on the Internet can't explain it, and I haven't studied it. I will continue to study it in the future and try to write a column to discuss the Connection reset problem and its solutions.

Keywords: Java socket Back-end Multithreading

Added by feckless on Sat, 05 Feb 2022 20:49:22 +0200