Java Socket programming from single thread to multi thread to realize chat applet

Posted by codebuilder on Sat, 05 Feb 2022 02:51:37 +0100

Single threaded example

Realize function

For 1-to-1 chat communication between the server and the client, you need to run the server first and then the client. When the user enters bye, close the socket and terminate the chat.

Specific operation process

  1. Start the server, create a ServerSocket, block the accept method, and wait for the client to connect.
  2. Start the client and connect to the server.
  3. The server accepts the connection and gets the ServerSocket socket object returned by accept().
  4. The server side and the client side create their own threads to listen to messages, and then they can interact with each other through the input and output stream of the socket.
  5. When one party inputs bye, the socket is closed and disconnected
  6. Close relevant resources and the communication ends

code

Server side

//ServerDemo.java
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
 * @author Pure_vv
 * @date 2022/1/29
 */
public class ServerDemo {
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(SocketUtils.PORT);
            Socket socket = serverSocket.accept();

            //Start receiving message thread
            new ReceiveMsg(socket).start();

            //The main thread starts sending messages
            SocketUtils.sendMsg(socket, socket.getOutputStream());
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

client

//ClientDemo.java
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
/**
 * @author Pure_vv
 * @date 2022/1/29
 */
public class ClientDemo {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket(InetAddress.getLocalHost(),SocketUtils.PORT);
            //Receive message thread on
            new ReceiveMsg(socket).start();

            //The main thread starts sending messages
            SocketUtils.sendMsg(socket, socket.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Encapsulated tool class

SocketUtils.java: encapsulates the method of sending user console input messages and defines port constants.

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
/**
 * @author Pure_vv
 * @date 2022/2/4
 */
public class SocketUtils {
    public static final int PORT = 8888;
    public static void sendMsg(Socket socket, OutputStream os){
        PrintWriter pw = new PrintWriter(os);
        Scanner sc = new Scanner(System.in);
        String msg;
        while (!socket.isClosed() && (msg = sc.next())!=null){
            // BufferedReader. The readline() method does not stop reading until it encounters \ n or \ r, which is a blocking method
            //Manually encapsulate messages and add \ n
            pw.write(msg+'\n');
            pw.flush();
            if ("bye".equals(msg)){
                break;
            }
        }
        System.out.println("The message transmission is completed and the program ends");
        try {
            os.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ReceiveMsg.java: create a new thread to listen for information and print the information to the console

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.SocketException;
/**
 * @author Pure_vv
 * @date 2022/1/29
 */
public class ReceiveMsg extends Thread {
    InputStream inputStream;
    Socket socket;
    public ReceiveMsg(Socket socket) {
        this.socket = socket;
        try {
            this.inputStream = socket.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
        System.out.println("Thread started");
        //Byte stream character stream
        InputStreamReader isr = new InputStreamReader(inputStream);
        //Add buffered read data
        BufferedReader br = new BufferedReader(isr);
        try {
            String msg;
            //Print the message to the console when it is received
            while ((msg=br.readLine())!=null) {
                System.out.println("msg:"+msg);
                if ("bye".equals(msg)){
                    System.out.println("receive messages socket close");
                    socket.close();
                    break;
                }
            }
        } catch (IOException e) {
            if (!(e instanceof SocketException)){
                e.printStackTrace();
            }
        }
    }
}

Multithreading example

Enable the server to support multiple client connections and chat

Just add a thread class and slightly change serverdemo java

//ServerDemo.java
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
 * @author Pure_vv
 * @date 2022/1/29
 */
public class ServerDemo {
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(SocketUtils.PORT);
            while (true){
                Socket socket = serverSocket.accept();
                // Start a new thread to listen for connection requests
                new ServerThread(socket).start();
            }
//            new ReceiveMsg(socket).start();
//            SocketUtils.sendMsg(socket, socket.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Added thread class serverthread java

import java.io.IOException;
import java.net.Socket;
/**
 * @author Pure_vv
 * @date 2022/2/5
 */
public class ServerThread extends Thread{
    Socket socket;
    public ServerThread(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        super.run();
        //Start receiving message thread
        new ReceiveMsg(socket).start();
        //The main thread starts sending messages
        try {
            SocketUtils.sendMsg(socket, socket.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

reflection

What I don't quite understand:

  • Use PrintWriter to transmit messages (for example, from the server to the client). After the client's socket is closed, the server will not throw an exception when sending messages without understanding

  • Solution: the server closes the socket after receiving the bye and judges the socket when sending a message isClosed()

But at the same time, this will bring a problem. When chatting with a single thread, you can pay attention to the code of sending messages

while (!socket.isClosed() && (msg = sc.next())!=null){
	pw.write(msg+'\n');
	pw.flush();
	if ("bye".equals(msg)){
	    break;
	}
}

After the server is started, the accept method will be blocked. After the client is started, the codes of both sides will be blocked in sc.next(), socket Isclosed() returned false. At this time, either party enters bye to close the socket, and the other party needs to enter a line to finish completely.

  • Possible solutions: realize event monitoring (observer mode) through the registration notification mechanism to monitor whether the socket is closed in real time. It needs to be implemented and verified

In addition, there is a problem. When using multithreading, it will cause confusion in chat. Two threads connecting to the client run concurrently, which will cause the server to need to input alternately.

reference material:

The Scanner class throws Java util. NoSuchElementException

Java thread listening events_ How does event monitoring in java realize monitoring at any time? Is it through threads

java - use java net. Socket and java net. ServerSocket implements a simple chat program

Topics: Java network socket Back-end server