This is the 8th day of my participation in the August More text Challenge. For details, see: August More Text Challenge

Introduces the concept of Scoekt, and provides a simple communication program based on TCP and UDP Protocol Java Socket API, such as simple chat room.

Previously, we have simply learned various protocols. We know that most application layer protocols, such as HTTP, FTP, SMTP, POP3, etc., all rely on the lower transport layer TCP/UDP protocol for data transmission, so in fact, we can directly use TCP/UDP protocol for network communication. And most languages already provide a ready-made TCP/UDP programming API, called Socket. The following simple learning can not rely on the application layer protocol for network communication Socket programming.

1 summary of the Socket

Socket translates into Chinese as a Socket. Socket itself is not a protocol, but an interface (API), it just provides an interface for TCP/UDP programming, it will be complex TCP/UDP protocol operations hidden, we only need to follow the Socket development regulations to programming, The program written naturally follows the TCP/UDP protocol, naturally can be two computers to communicate with each other, which is similar to the design pattern in the facade pattern!

In other words, Socket hides the interaction details of various TCP/IP protocols and provides the upper layer interfaces of various high-level languages for TCP/UDP programming. It can receive requests and send responses and realize the communication between different computers. In fact, the underlying protocol originally provided an interface for network programming, but it was too low-level and unfriendly to many programmers, especially those who used upper-level languages such as Java. As a result, the Socket interface and its associated APIS emerged. Scoket further encapsulated the interfaces of these underlying protocols. And it’s open through high-level language apis, making it more user-friendly for ordinary programmers who need to do network programming.

Different languages provide implementations of the Socket API. Java also has classes such as java.net.Socket and java.net.DatagramSocket.

Java sockets are classified as follows:

  1. Stream socket (SOCK_STREAM) : Stream sockets are used to provide connection-oriented, reliable data transfer services. The service will ensure that data can be sent error-free, repeatable, and received sequentially. Streaming sockets enable reliable data services because they use the transmission control protocol, TCP.
  2. Datagram socket (SOCK_DGRAM) : Datagram sockets provide a connectionless service. The service does not guarantee the reliability of data transmission. Data may be lost or duplicated during transmission, and data cannot be received sequentially, but the transmission efficiency is high. Datagram sockets use THE UDP protocol to transmit data.
  3. Raw sockets: Raw sockets are primarily used in the development of protocols that allow lower-level operations. It is powerful, but not as easy to use as the two sockets described above, and ordinary programs do not involve raw sockets.

2 the Socket communication

The Socket contains five types of information necessary for network communication: the protocol used for connection, the IP address of the local host, the protocol port of the local process, the IP address of the remote host, and the protocol port of the remote process.

Socket is the encapsulation of TCP/IP protocol. Therefore, according to the communication protocol we learned earlier, the simplest Socket programming does not use the upper-layer – application layer related protocols, such as HTTP, SMTP, POP3 and so on. This is because we can only use the protocols at the transport layer and below when transferring data, rather than the protocols at the application layer.

If the Socket uses THE TCP protocol for communication, then the Socket program will also involve the three-way handshake and four-way wave of THE TCP connection. The TCP based Socket communication flowchart is as follows:

Although we can directly use sockets to communicate and transmit data to each other, the data transmitted or received by sockets are byte data. Without the application layer and its related protocols, we cannot identify the type, content and format of the original data corresponding to the byte data transmitted. There is no way to restore the baty bytes to the original data.

Therefore, if you want to make the transmitted data meaningful, you must use application layer protocols. There are many application layer protocols, such as HTTP, FTP, and TELNET, which are used to transmit different data and serve different applications. Our Web applications use HTTP as the application layer protocol to encapsulate HTTP packet information, and then use TCP/IP as the transport layer protocol to send it to the server or client.

The upper layer application/protocol communication needs to rely on the lower layer protocol, if we need to use the application layer protocol to communicate. So what to do? In fact, sockets already provide interfaces for connecting to upper-layer application layer protocols, and JDK has provided us with good encapsulation classes for upper-layer protocols, such as HttpURLConnection, URL, HttpClient and other API based on HTTP protocol encapsulation. Such as FtpClient tool class based on FTP protocol encapsulation, such as SmtpClient tool class based on SMTP protocol encapsulation…… . The bottom layer basically ends up calling the Socket API of the JDK for data transfer, and all these application-layer protocol utility classes need to do is either encode data for transfer through the Socket or decode the data they receive in their own format.

In fact, almost all languages currently use sockets at the bottom of their Web applications to communicate.

3 Use THE UDP protocol for communication

Use UDP protocol for data transmission, mainly using two classes, one is DatagramSocket, the other is DatagramPacket.

3.1 related classes

3.1.1 InetAddress IP address class

public class InetAddress extends Object implements Serializable

In the JavA.net package, this class represents an abstraction of an Internet Protocol (IP) address. The InetAddress object encapsulates the IP address. An IP address is a 32-bit or 128-bit unsigned number used by the IP protocol, a lower-level protocol on which UDP and TCP are built. No construction method.

3.1.1.1 Obtain the InetAddress object

public static InetAddress getLocalHost()

Returns the IP address of the local host. Get the InetAddress object. It consists of the host name and IP address. Such as: DESKTOP – 8 q842hn / 192.168.253.1

public static InetAddress getByName(String host)

Determine the IP address of the host given the host name. Get the InetAddress object.

The host name can be passed as “desktop-8q842hn”, and the InetAddress object consisting of the machine name/IP address is returned: esktop-8q842hn /192.168.253.1

It can also be the text representation of the incoming IP address, such as 192.168.253.1. The InetAddress object consisting of/IP address is returned. However, you can still use this object to obtain the host name by method: /192.168.253.1

You can also pass in a domain name, such as www.baidu.com. Return an Inet composed of domain name/IP address…

3.1.1.2 Obtain the local Ip address and host name

String getHostAddress() The IP address is a String. Returns the original IP address in string format.
String getHostName() Return the host name for this IP address; If the security check does not allow the operation, the text representation of the IP address is returned.
String toString() The string returned has the following form: host name/literal IP address.

In addition, there is an InetSocketAddress class, which represents the IP socket address (IP address + port number), and it can also be a pair (host name + port number), in which case an attempt will be made to resolve the host name. If the resolution fails, the address is considered unresolved, but it can still be used in certain situations, such as through a proxy connection.

3.1.2 DatagramSocket The DatagramSocket class

public class DatagramSocket extends Object

Located in the javA.NET package. This class represents the socket used to send and receive datagram packets. It is also called a datagram socket. A datagram socket is the sending or receiving point of a packet delivery service that sends packets using the UDP protocol.

3.1.2.1 constructor

DatagramSocket() Construct the datagram socket and bind it to any available port on the local host. The socket is bound to a wildcard address, and the IP address is selected by the kernel.
DatagramSocket(int port)

Port – Indicates the port to be used.
Creates a datagram socket and binds it to the specified port on the local host.

3.1.2.2 API methods

void send(DatagramPacket p) Send datagram packets from this socket. The DatagramPacket contains information indicating the data to be sent, its length, the IP address of the remote host, and the port number of the remote host.
void receive(DatagramPacket p) Receive datagram packets from this socket. When this method returns, the buffer of the DatagramPacket has been filled with the received data. The datagram packet also contains the IP address of the sender and the port number on the sender’s machine. This method blocks until the datagram packet is received.
InetAddress getLocalAddress() Returns the local address to which the socket is bound, or an InetAddress representing any local address if the socket is not bound.
int getPort() Returns the port for this socket. If the socket is not connected, return -1.

3.1.3 DatagramPacket Data packet class

public final class DatagramPacket extends Object

This class represents datagram packets. Datagram packets are used to implement the connectionless packet delivery service. Sending multiple datagram packets does not guarantee sequence or integrity. The number of sending times must be the same as the number of receiving times; otherwise, data will be lost.

1.3.1 constructor

DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port);

Buf – Package data.

Offset – The offset of packet data.

Length – Indicates the length of packet data.

Address – Indicates the destination address.

Port – Indicates the destination port number.
Constructs a datagram used to send a packet of length and offset to the specified port on the specified host. The length parameter must be less than or equal to buf.length.
DatagramPacket(byte[] buf,int length)

Buf – Buffer to save incoming datagrams.

Len – Number of bytes to read.
Construct DatagramPacket to receive packets of length. The length parameter must be less than or equal to buf.length.

3.1.3.2 API methods

InetAddress getAddress() Returns the IP address of the machine to which the datagram is to be sent or received.
int getPort() Returns the port number of a remote host to which the datagram is to be sent or received.
byte[] getData() Returns the data buffer. Data received or to be sent starts at offset in the buffer and lasts length.
int getLength() Returns the length of the data to be sent or received.
int getOffset() Returns the offset of the data to be sent or received.

3.2 Basic Cases

3.2.1 UDP Sender

public class Sender {
    public static void main(String[] args) throws IOException, IOException {
        //1. Create a sender datagram socket socket for sending datagram packets. If a port number is specified, the port number of the sending end is specified. If you do not specify a port number, the system assigns one by default.
        DatagramSocket ds = new DatagramSocket();
        //2. Construct a datagram containing a byte array of the data to be sent, the starting index, the length of the data, the IP address of the specified remote host [InetAddress object], and the port number on the remote host.
        DatagramPacket dp = new DatagramPacket("Hello".getBytes(), 0."Hello".getBytes().length, InetAddress.getLocalHost(), 8888);
        //3. Send datagram packets
        ds.send(dp);
        //4. Close the socketds.close(); }}Copy the code

3.2.2 UDP Receiver

public class Receiver {
    public static void main(String[] args) throws IOException {
        // Create the receiver datagram socket socket. You must specify the receiver port number
        DatagramSocket ds = new DatagramSocket(8888);
        while (true) {    // Receive data in a loop
            // Construct an empty datagram for receiving data: internally uses an array of bytes as a buffer for receiving data. You can specify the starting index and the number of bytes to read.
            // If the amount of data sent exceeds the size of the receiving space, the data will be lost
            byte[] by = new byte[1024];
            DatagramPacket dp = new DatagramPacket(by, 0, by.length);
            // Receive data: store data in datagram packets. This method will remain blocked until the data is received!
            ds.receive(dp);
            // Open the datagram and get the data buffer array, which will fetch all the data sent at once
            byte[] data = dp.getData();
            //dp.getLength(), which refers to the length of the received data.
            System.out.println("data: " + new String(data, 0, dp.getLength()));
            // Obtain the IP address of the sender
            String hostName = dp.getAddress().getHostName();
            System.out.println("hostName: " + hostName);
            // Get the host name of the sender
            String hostAddress = dp.getAddress().getHostAddress();
            System.out.println("hostAddress: " + hostAddress);
            // Get the sending port number
            int port = dp.getPort();
            System.out.println("port: " + port);
        }
        //ds.close(); The receiver should always be on to receive data}}Copy the code

3.3 UDP simple chat room

Receiving services:

public class ReceiveServer implements Runnable {
    private final DatagramSocket dsReceive;

    public ReceiveServer(DatagramSocket dsReceive) {
        this.dsReceive = dsReceive;
    }

    @Override
    public void run(a) {
        while (true) {
            // Construct an empty datagram for receiving data: internally uses an array of bytes as a buffer for receiving data. You can specify the starting index and the number of bytes to read.
            byte[] by = new byte[1024];
            DatagramPacket dp = new DatagramPacket(by, 0, by.length);
            // Receive data: store data in datagram packets. This method will remain blocked until the data is received!
            try {
                dsReceive.receive(dp);
            } catch (IOException e) {
                e.printStackTrace();
            }
            // Open the datagram to get the data buffer array
            byte[] byteData = dp.getData();
            //dp.getLength(), which refers to the length of the received data.
            String data = new String(byteData, 0, dp.getLength());
            // Get time
            String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            // Obtain the host IP address of the sender
            String hostAddress = dp.getAddress().getHostAddress();
            // Get the send port number
            int port = dp.getPort();
            System.out.println(time + "-" + hostAddress + ":"+ port); System.err.println(data); }}}Copy the code

Sending service:

public class SendServer implements Runnable {

    private final DatagramSocket dsSend;
    private final InetSocketAddress inetSocketAddress;

    public SendServer(DatagramSocket dsSend, InetSocketAddress inetSocketAddress) {
        this.dsSend = dsSend;
        this.inetSocketAddress = inetSocketAddress;
    }

    @Override
    public void run(a) {
        try {
            // Receive keyboard input data
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            String str;
            while((str = br.readLine()) ! =null) {
                // Construct a datagram containing a byte array of the data to be sent, the starting index, the length, the specified remote IP, and the port number on the remote IP.
                DatagramPacket dp = new DatagramPacket(str.getBytes(), 0, str.getBytes().length, inetSocketAddress);
                // Send datagram packets
                dsSend.send(dp);
                // Define the closing statement
                if (str.equals("886")) {
                    break; }}}catch (IOException e) {
            e.printStackTrace();
        } finally {
            // Close the socket socketdsSend.close(); }}}Copy the code

Client 1:

public class ChatClient1 {
    public static void main(String[] args) throws SocketException, UnknownHostException {
        // Send the service to the receiving service with the specified Ip address and port. Here the IP is the local machine, port 9999
        String hostName = InetAddress.getLocalHost().getHostName();
        InetSocketAddress inetSocketAddress = new InetSocketAddress(hostName, 9999);
        SendServer st = new SendServer(new DatagramSocket(), inetSocketAddress);
        // Receive service, receive port 9999
        ReceiveServer rt = new ReceiveServer(new DatagramSocket(8888));
        new Thread(st).start();
        newThread(rt).start(); }}Copy the code

Client 2:

public class ChatClient2 {
    public static void main(String[] args) throws SocketException, UnknownHostException {
        // Send the service to the receiving service with the specified Ip address and port. Here the IP is the local machine, port 8888
        String hostName = InetAddress.getLocalHost().getHostName();
        InetSocketAddress inetSocketAddress = new InetSocketAddress(hostName, 8888);
        SendServer st = new SendServer(new DatagramSocket(), inetSocketAddress);
        // Receive service, receive port 9999
        ReceiveServer rt = new ReceiveServer(new DatagramSocket(9999));
        new Thread(st).start();
        newThread(rt).start(); }}Copy the code

4 Use THE TCP protocol for communication

Note: To use TCP transport, you must first start the server, because the data of the transport must be received, otherwise an exception will be thrown. UDP does not guarantee that data will be received, so no error will be reported even if the sender is enabled first!

Use TCP protocol for data transmission, mainly with Socket and ServerSocket, as well as input and output streams!

4.1 related classes

4.1.1 Socket Socket classes

public class Socket extends Object

Java Socket class for TCP requests.

A socket is an endpoint for communication between two machines. This class implements the client socket, and this class socket is based on the TCP protocol, data is transmitted through the stream, so it is also called stream socket. A uniquely identified IP address and port number on the network are combined to form a uniquely identifiable identifier socket.

There are sockets at both ends of the communication. In fact, network communication is the communication between sockets. Data is transmitted between the two sockets through IO streams.

4.1.1.1 the constructor

Socket(String host,int port)

Host-host name (string IP address) : indicates the string IP address of the server.

Port – (application) port number.
Creates a stream socket and connects it to the specified (application) port number on the specified host.
Socket(InetAddress address,int port)

Address – IP address of the InetAddress class server.

Port – (application) port number.
Creates a stream socket and connects it to the specified port number at the specified IP address.

4.1.1.2 API methods

OutputStream getOutputStream() Returns the output stream for this socket.
InputStream getInputStream() Returns the input stream for this socket. If no data is read, this method will block.
InetAddress getInetAddress() Returns the address of the socket connection.
InetAddress getLocalAddress() Gets the local address to which the socket is bound.
int getPort() Returns the remote port to which this socket is connected.
int getLocalPort() Returns the local port to which this socket is bound.
void shutdownInput() The input stream for this socket is placed at “end of stream”. Any data sent to the socket’s input stream is acknowledged and silently discarded.
void shutdownOutput() Disables the output stream for this socket. For TCP sockets, any previously written data is sent, followed by TCP’s normal connection termination sequence.
void close() Close this socket.

4.1.2 ServerSocket ServerSocket class

public class ServerSocket extends Object

This class implements server sockets. The server socket waits for the request to pass through the network. It performs some action based on the request, and then perhaps returns the result back to the requester.

The constructor:

ServerSocket(int port) Creates a server (application) socket bound to a specific port.

API methods:

Socket accept() Listen for and accept connections to this socket. This method blocks until it is successfully connected and returned by the client, returning the client socket that can receive data from the client or send a response message to the client
void close() Close this socket.

4.2 Basic Cases

The server receives the data from the client and, in response, sends the data to the client.

4.2.1 TCP Server

public class Server {
    public static void main(String[] args) throws IOException {
        //1. Create a server socket and bind it to a port number
        ServerSocket ss = new ServerSocket(8888);
        //2. Listen for client connections and return the corresponding socket object. This method blocks until successfully connected by the client and returned!
        Socket a = ss.accept();
        // Obtain the client host name, IP address, and port
        InetAddress ia = a.getInetAddress();
        System.out.println("client :" + ia.getHostAddress() + ":" + a.getPort());
        // Create an input stream and use read() to read the data. This method blocks until the data is read!
        InputStream is = a.getInputStream();
        byte[] b = new byte[1024];
        int read = is.read(b);
        System.out.println("from client: " + new String(b, 0, read));


        // Respond to the client, get the output stream, and send the data
        OutputStream os = a.getOutputStream();
        os.write("Received".getBytes());
        os.flush();
        // Release the obtained socket resources. The server socket should not be closeda.close(); }}Copy the code

4.2.2 TCP Client

public class Client {
    public static void main(String[] args) throws IOException {
        //1. Create a client socket and connect it to the specified port at the specified IP address.
        Socket s = new Socket(InetAddress.getLocalHost(), 8888);
        // Wait 4 seconds before sending the message
        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(4));
        //2. Get the output stream and write the data
        OutputStream os = s.getOutputStream();
        os.write("Hello, did you get the data?".getBytes());
        os.flush();
        //socket.shutdownOutput(); This method is required when the server receives in a loop
        // Get the input stream and use read() to read the response from the server. This method blocks until the data is read!
        InputStream is = s.getInputStream();
        byte[] by = new byte[1024];
        int read;
        while((read = is.read(by)) ! = -1) {
            System.out.println("from server: " + new String(by, 0, read));
        }
        // Close the client to release resourcess.close(); }}Copy the code

4.3 Text Upload

Client: upload a text, server: save and respond!

Client:

public class TxtClient {
    public static void main(String[] args) throws IOException {
        Socket s = new Socket(InetAddress.getLocalHost(), 8888);
        // Read the text into the stream, reading the location of the text
        BufferedReader br = new BufferedReader(new FileReader("E:\\Idea\\Java-EE\\WebProgram\\src\\main\\resources\\a.txt"));
        // Client output stream
        OutputStream os = s.getOutputStream();
        PrintWriter pw = new PrintWriter(os);
        String str;
        while((str = br.readLine()) ! =null) {
            // Write the text data to the client output stream and send it to the server
            pw.println(str);
            pw.flush();
        }
        s.shutdownOutput();
        /* * Receive server response */
        // Client input stream
        InputStream is = s.getInputStream();
        BufferedReader br1 = new BufferedReader(new InputStreamReader(is));
        String str1;
        while((str1 = br1.readLine()) ! =null) { System.out.println(str1); } s.close(); }}Copy the code

Server:

public class TxtServer {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(8888);
        Socket a = ss.accept();
        // File output stream, specifying the file name and path to upload
        PrintWriter pw = new PrintWriter("E:\\Idea\\Java-EE\\WebProgram\\src\\main\\resources\\b.txt");
        // The server input stream
        InputStream is = a.getInputStream();
        // Convert to a buffered stream
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String str;
        System.out.println("The contents of the file are:");
        while((str = br.readLine()) ! =null) {
            System.out.println(str);
            // The server saves the data to the specified place
            pw.println(str);
            pw.flush();
        }
        /* * Respond to the client */
        // The server output stream
        PrintWriter pw1 = new PrintWriter(a.getOutputStream());
        pw1.println("-- -- -- -- -- -- -- -- -- -- -");
        pw1.println("File uploaded"); pw1.flush(); a.close(); }}Copy the code

4.4 Picture Uploading

The server uses multithreading technology to process the client request to prevent blocking when multiple client requests appear simultaneously.

Client:

public class PicClient {
    public static void main(String[] args) throws IOException {
        String filePath = "E: \ \ Idea \ \ Java - EE \ \ WebProgram \ \ SRC \ \ the main \ \ resources \ \ QQ picture 20201123093907. The PNG";
        savePic(filePath);
    }

    static void savePic(String filePath) throws IOException {
        File pictureFile = new File(filePath);
        if(! pictureFile.exists()) { System.out.println("The file you uploaded does not exist.");
            return;
        }
        if(! pictureFile.isFile()) { System.out.println("You're not uploading a file.");
            return;
        }
        if(! pictureFile.getName().endsWith(".jpg")) {
            System.out.println("You're not uploading a JPG file.");
            return;
        }
        if (pictureFile.length() > 1024 * 1024 * 3) {
            System.out.println("Upload image size limit, no more than 3M");
            return;
        }
        // Create the client
        Socket s = new Socket(InetAddress.getLocalHost(), 8888);
        // Prepare an input stream to read the image
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(pictureFile));
        // Prepare a client output stream to transmit data
        BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
        // Prepare a client input stream to receive response data from the server
        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        // Send the picture data
        byte[] by = new byte[1024];
        int len;
        while((len = bis.read(by)) ! = -1) {
            bos.write(by, 0, len);
            bos.flush();
        }
        s.shutdownOutput();
        // Receive the server responseString readLine = br.readLine(); System.out.println(readLine); }}Copy the code

Server:

public class PicServer implements Runnable {

    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(8888);
        while (true) {
            // The accept method is blocked until the link is passed in, so it does not loop indefinitely
            Socket a = ss.accept();
            // Use multiple threads to handle client connections to prevent client requests from blocking
            THREAD_POOL_EXECUTOR.submit(newPicServer(a)); }}private Socket socket;

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

    static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(5.10.60, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),
            Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

    @Override
    public void run(a) {
        // Client connected feedback
        System.out.println(socket.getInetAddress().getHostName() + "Connected");
        // Name the file uUID
        String fileName = UUID.randomUUID().toString().replaceAll("-"."").toUpperCase();
        try {
            // Client input stream
            BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
            // File output stream
            BufferedOutputStream bos = new BufferedOutputStream(
                    new FileOutputStream(("E:\\Idea\\Java-EE\\WebProgram\\src\\main\\resources\\" + fileName + ".jpg")));
            byte[] b = new byte[1024];
            int read;
            while((read = bis.read(b)) ! = -1) {
                bos.write(b, 0, read);
                bos.flush();
            }
            OutputStream os = socket.getOutputStream();
            // Client output stream
            PrintWriter pw = new PrintWriter(os);
            pw.println(socket.getLocalAddress().getHostName() + "File uploaded");
            pw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch(IOException e) { e.printStackTrace(); }}}}Copy the code

4.5 TCP implements simple multiplayer chat rooms

You need to start the server first and then multiple clients. The format of the message is name:message. Name indicates the name of another user, which is used for private chat. When the name is all, the message is sent to a group chat.

Server:

public class ChatServer {

    /** * Server saves all users */
    private static HashSet<User> users = new HashSet<>();


    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8888);

        // Loop through the connection
        while (true) {
            // A connection represents a user
            Socket accept = serverSocket.accept();
            User user = new User(accept);
            users.add(user);
            newThread(user).start(); }}/** * A connection represents a user and can forward messages */
    private static class User implements Runnable {

        public User(a) {}// Record the name of the connected user
        private String name;

        public String getName(a) {
            return name;
        }

        // Responsible for receiving
        private DataInputStream is;
        // Responsible for sending
        private DataOutputStream os;

        public User(Socket client) throws IOException {
            is = new DataInputStream(client.getInputStream());
            os = new DataOutputStream(client.getOutputStream());
            name = is.readUTF();
            this.send("Welcome" + name + "Enter the chat room".true.false);
            this.send("You have entered the chat room.".true.true);
        }

        /** * receives the message and forwards it to the corresponding user */
        @Override
        public void run(a) {
            while (true) {
                try {
                    this.send(this.revice(), false.false);
                } catch (IOException e) {
                    users.remove(this);
                    try {
                        is.close();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                    try {
                        os.close();
                    } catch(IOException ioException) { ioException.printStackTrace(); } e.printStackTrace(); }}}// Receive the message
        public String revice(a) throws IOException {
            return is.readUTF();
        }


        /** * Send message **@paramMSG Original message * If a user is not a system user, the original message of a common user is sent in the form of name: MSG. If the name is all, the original message is sent to all online users@paramSystem Whether the system message *@paramIsPrivate Whether private chat */
        public void send(String msg, boolean system, boolean isPrivate) throws IOException {
            if (system) {
                if (isPrivate) {
                    send("System" + ":" + isPrivate + ":" + msg);
                    return;
                }
                for (User client : users) {
                    client.send("System" + ":" + isPrivate + ":"+ msg); }}else {
                if (msg.contains(":")) {
                    String[] split = msg.split(":");
                    if ("all".equals(split[0]) {for (User client : users) {
                            if(client ! =this) {
                                client.send(this.name + ":" + isPrivate + ":" + split[1]); }}}else {
                        for (User user : users) {
                            if (user.getName().equals(split[0])) {
                                user.send(this.name + ":" + !isPrivate + ":" + split[1]);
                            }
                        }
                    }
                }
            }
        }

        /** * Send messages **@paramMSG Indicates the final message in the format of name:isPrivate: MSG */
        public void send(String msg) throws IOException { os.writeUTF(msg); os.flush(); }}}Copy the code

Client:

public class ChatClient {

    Socket socket;
    DataInputStream dataInputStream;
    DataOutputStream dataOutputStream;

    public ChatClient(Socket socket, String name) throws IOException {
        this.socket = socket;
        dataInputStream = new DataInputStream(socket.getInputStream());
        dataOutputStream = new DataOutputStream(socket.getOutputStream());

        new Thread(new SendServer(name)).start();
        new Thread(new ReceiveServer()).start();
    }

    /** * The client receives messages */
    class ReceiveServer implements Runnable {

        @Override
        public void run(a) {
            while (true) {
                try {
                    String data = dataInputStream.readUTF();
                    // Get time
                    String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                    // Get the send port number
                    String[] strs = data.split(":");
                    String type = "News";
                    if ("true".equals(strs[1])) {
                        type = "Private chat";
                    }
                    System.out.println(time + "-" + "From" + strs[0] + "The" + type);
                    System.out.println("\t" + strs[2]);
                } catch(IOException e) { e.printStackTrace(); }}}}/** * The client sends messages */
    class SendServer implements Runnable {

        public SendServer(String name) throws IOException {
            send(name);
        }

        @Override
        public void run(a) {
            try {
                // Receive keyboard input data
                BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
                String str;
                while((str = br.readLine()) ! =null) {
                    dataOutputStream.writeUTF(str);
                    dataOutputStream.flush();
                    // Define the closing statement
                    if (str.contains("886")) {
                        break; }}}catch (IOException e) {
                e.printStackTrace();
            } finally {
                // Close the socket socket
                try {
                    socket.close();
                } catch(IOException e) { e.printStackTrace(); }}}public void send(String msg) throws IOException { dataOutputStream.writeUTF(msg); }}}Copy the code

Test client:

public class ChatTest {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
        new ChatClient(socket, "ChatClient1");
    }

    public static class ChatClient2 {
        public static void main(String[] args) throws IOException {
            Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
            new ChatClient(socket, "ChatClient2"); }}public static class ChatClient3 {
        public static void main(String[] args) throws IOException {
            Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
            new ChatClient(socket, "ChatClient3"); }}}Copy the code

If you need to communicate, or the article is wrong, please leave a message directly. Also hope to like, collect, follow, I will continue to update a variety of Java learning blog!