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.
- 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.