Java UDP uses ECs to write simple chat rooms

2.1 UDP uses ECs to write simple chat rooms

In the past, I learned network programming and wrote a series of notes. But I didn't rent a server at that time, so I didn't really learn network programming at all.

This time, I will build the server program I wrote on the alicloud server I rented. Then we made a series of attempts. Among them, I think the best thing I wrote is to use UDP to make a simple chat room using ECs. Of course, this chat room is limited to one-on-one. That is, we open the client on the local machine, and then open the server on the server for one-to-one information transmission.

  1. Server side
import java.io.BufferedReader;
import java.io.DataOutput;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;

public class Server {
    public static int daPort = 0; // NAT dynamically allocated ports
    public static InetAddress  publicIP = null; // Public IP converted from NAT
    public static DatagramSocket socket = null;
    public static void main(String[] args) throws IOException {
        /* A socket UDP service is created by port 10000 */
        socket = new DatagramSocket(10000);

        /* Create a SendMsg thread and make it a daemon thread! Enable this thread to monitor sending messages */
        SendMsg sendMsg = new SendMsg();
        sendMsg.setDaemon(true);
        sendMsg.start();

        /* The following is the monitoring of receiving messages, which is managed by the main thread */
        while(true){
            byte[] container = new byte[1024];

            DatagramPacket packet = new DatagramPacket(container,0,container.length);

            // Block receiving packets sent by UDP
            socket.receive(packet);

            // Store the data of the packet in the corresponding type of container
            byte[] data = packet.getData();

            // Convert String to facilitate output
            String dataString = new String(data,0,getSize(data));

            // If we receive a heartbeat packet, we will not output it, but update our dynamically allocated port and public IP
            if(dataString.equals("HeartBeat")){
                //System.out.println("HeartBeat received");
                daPort = packet.getPort();
                publicIP = packet.getAddress();
            }else if(dataString.equals("bye")){
                // When we receive the bye string, we should quit! Going to shut down the socket service. This is a string password.
                break;
            }else{
                // Send normally received data
                System.out.println(dataString);
            }

        }
        // In fact, it doesn't matter whether there is a bye password or not. We just need to disconnect the client.
        socket.close();
    }


    // Read the valid data bits of byte [] array to facilitate better conversion to String type
    public static int getSize(byte[] data){
        int i = 0;
        for(byte x: data){
            if(x != (byte)0){
                i++;
            }
        }
        return i;
    }

    // The SendMsg Thread inherits the Thread in order to save trouble and has few threads
    public static class SendMsg extends Thread{

        @Override
        public void run() {
            super.run();

            while(true){
                BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

                String data = null;
                try {
                    data = reader.readLine();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                byte[] datas = data.getBytes();

                DatagramPacket packet  = new DatagramPacket(datas,0,datas.length,publicIP, daPort);

                try {
                    socket.send(packet);
                    System.out.println("Sending packet completed!");
                    //socket.send(packet);
                } catch (IOException e) {
                    e.printStackTrace();
                }

                if(data.equals("bye")){
                    System.out.println("Closed socket Service!");
                    break;
                }
            }
            socket.close();
        }
    }
}

Here are a few details to note. We receive packets all the time, but we judge a string HeartBeat. What is this? This is actually the HeartBeat packet we sent. That is, send a data packet every other period of time, commonly known as HeartBeat packet.

① So why does the client send heartbeat packets?
A: because UDP connections remain active for a short time. So we need to send a packet every other period of time to keep the connection active.

② Why get the IP and Port of the sent packet?
A: because our local machine belongs to the intranet, it needs to dynamically allocate ports through NAT and convert to external IP. In this case, we temporarily believe that the connection and communication between the two sides are safe based on this external IP and dynamically allocate ports. It can send and receive data effectively.

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;

public class client {
    // Improve scope
    public static JTextArea textArea = null;
    public static DatagramSocket socket = null;
    public static void main(String[] args) throws IOException {
        // Draw a simple JFrame GUI
        JFrame frame = new JFrame("UDP client");
        frame.setBounds(500,500,500,500);
        Container container = frame.getContentPane();
        container.setLayout(new GridLayout(3,1));
        JTextArea textAreaA = new JTextArea("Please enter text",10,40);
        JPanel jPanel1 = new JPanel();
        JPanel jPanel2 = new JPanel();
        JPanel jPanel3 = new JPanel();
        jPanel3.setLayout(new GridLayout(1,2));
        JTextArea textAreaB = new JTextArea("Data from the server:\n",10,40);
        textAreaA.setLineWrap(true);
        textAreaB.setLineWrap(true);
        JScrollPane jScrollPaneA = new JScrollPane(textAreaA);
        JScrollPane jScrollPaneB = new JScrollPane(textAreaB);
        textAreaB.setEditable(false);
        jPanel1.add(jScrollPaneA);
        jPanel2.add(jScrollPaneB);
        container.add(jPanel1);
        container.add(jPanel2);

        JButton bthSend = new JButton("Click send message");
        JButton bthClose = new JButton("Safely close the connection");

        jPanel3.add(bthSend);jPanel3.add(bthClose);
        container.add(jPanel3);

        // Specify a port to open the socket UDP service
        socket = new DatagramSocket(10000);

        // Get textAreaB
        textArea = textAreaB;
        // Create a receiveMsg thread to monitor the received data
        ReceiveMsg receiveMsg = new ReceiveMsg();
        // Set as daemon thread
        receiveMsg.setDaemon(true);
        receiveMsg.start();

        // Create a heartBeat thread and send a heartBeat packet every 30 seconds
        HeartBeat heartBeat = new HeartBeat();
        // Set as daemon thread
        heartBeat.setDaemon(true);
        heartBeat.start();

        // For the initial connection, we must send a data packet for connectivity verification. Only when we receive this information at the server can we prove that we are connected
        byte[] datas = "Connection succeeded".getBytes();
        DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("Cloud server IP", 10000));
        socket.send(packet);

        // Trigger monitoring of send button
        bthSend.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String msg = textAreaA.getText();
                byte[] datas = msg.getBytes();

                DatagramPacket packet = new DatagramPacket(datas,0,datas.length,
                        new InetSocketAddress("Cloud server IP", 10000));
                try {
                    socket.send(packet);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
                System.out.println(packet.getPort());

                textAreaA.setText("");
            }
        });

        // Turn off trigger listening of service and thread buttons
        bthClose.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if(!socket.isClosed()){
                    socket.close();
                }
                receiveMsg.stop();

                frame.setTitle("All connections have been disconnected! Thread closed!");
            }
        });


        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public static class ReceiveMsg extends Thread{
        @Override
        public void run() {
            super.run();
            while(true){
                byte[] containner = new byte[1024];
                DatagramPacket packet = new DatagramPacket(containner,0,containner.length);

                try {
                    socket.receive(packet);
                    //System.out.println(packet.getPort());
                } catch (IOException e) {
                    e.printStackTrace();
                }

                byte[] data = packet.getData();

                String dataString = new String(data,0,getSize(data));
                //System.out.println(dataString);
                textArea.append(dataString+"\n");
            }
        }
        // Read the valid data bits of byte [] array to facilitate better conversion to String type
        public static int getSize(byte[] data) {
            int i = 0;
            for(byte x:data)
            {
                if(x != (byte)0)
                {
                    i++;
                }
            }
            return i;
        }
    }

    public static class HeartBeat extends Thread{
        @Override
        public void run() {
            super.run();
            String heartBeat = "HeartBeat";
            while(true){
                DatagramPacket packet = new DatagramPacket(heartBeat.getBytes(), 0,heartBeat.length(),new InetSocketAddress("Cloud server IP", 10000));
                try {
                    socket.send(packet);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    // It is recommended to send heartbeat packets every 30 seconds because the UDP protocol life cycle is very short. Heartbeat packets must be sent at intervals to remain active
                    Thread.sleep(30000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Then our overall idea is to open the 10000 port of the ECS, and open a thread on the client and the server respectively. The sub thread of the client is used to receive the data packets sent by the server, and the sub thread of the server is used to send the data packets to the client.

But our client has to build another thread to send heartbeat packets. Keep the connection active.

In this way, we only open a 10000 port on the ECs to realize the packet communication between the ECS and the local machine.



Keywords: Java server udp

Added by Morpheus on Sun, 23 Jan 2022 05:16:03 +0200