Technology Sharing

[JavaEE] Network Programming - UDP

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

insert image description here
🤡🤡🤡个人主页🤡🤡🤡
🤡🤡🤡JavaEE专栏🤡🤡🤡

1. Datagram Sockets (UDP)

1.1 Features

  1. Connectionless: There is no need to establish a dedicated channel before data transmission. Each data packet is sent independently and chooses its own path to reach the destination. It is like sending a message. You only need to send the message and the other party does not need to receive it.
  2. Unreliable transmission: When transmitting data, there is no need to pay attention to whether the other party has received it, just send it.
  3. Data stream oriented: The basic unit of data transmission is a UDP datagram. Only a complete UDP datagram can be read or written at a time.
  4. Full-duplex: Two-way communication is possible in one link.

1.2 Coding

Socket APIs are all provided by the system. Different systems provide different APIs, but Java further encapsulates these system APIs.
The socket API in UDP focuses on two classes: DatagramSocket and DatagramPacket

1.2.1DatagramSocket

The role of this class can be seen as a remote control for "operating the network card", that is, reading and writing operations on the network card, which can be understood as reading and writing files.
Several methods are provided:
insert image description here

1.2.2DatagramPacket

This class describes UDP datagrams. A DatagramPacket object is equivalent to a UDP datagram. Sending and receiving once means transmitting a DatagramPacket object.

1.2.3 Implement a UDP (24*7) echo server

Echo: When the client sends different requests to the server, the server will return different responses. But in this case, the echo means that the server responds to the client's request without any calculation or business logic.

public class udpEchoServer {
    public DatagramSocket socket = null;
    public udpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
    public void  start() throws IOException {
        System.out.println("服务器启动!!!");
        while (true) {
            //接受客户端请求
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);
            //为了更好处理数据并方便打印,将接受的数据转化为字符串操作
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            //处理客户端的请求(算术计算或者业务逻辑)
            String response = this.process(request);
            //将处理完的请求返还给客户端
            DatagramPacket resposePacket = new DatagramPacket(response.getBytes(),0,response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(resposePacket);
            //打印客户端的ip地址端口号和接收客户端的数据以及处理完客户端后的数据
            System.out.printf("[%s:%d] req = %s,resp = %sn",requestPacket.getAddress(),requestPacket.getPort(),
                    request,response);
        }
    }
    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        udpEchoServer udpEchoServer = new udpEchoServer(9999);
        udpEchoServer.start();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

insert image description here
The DatagramPacket object represents a datagram. A UDP datagram consists of a header and a payload. The IP address and port number in the header are attributes of the DatagramPacket class. The payload is not provided by this class, so the programmer needs to provide it himself. Just like a monk begging for alms, you need to provide a bowl for begging, and then each family puts the food in the bowl. Since the essence of a datagram is binary data, I provide a byte array to store data as the payload.
insert image description here
The data received from the client needs to be returned to the client after being processed by the server, so a DatagramPacket object needs to be created to store the data processed by the server before the server sends it to the client. At this time, the DatagramPacket object does not need to provide a blank space, just put the processed data into this object. Since the client's IP address and port number are recorded when receiving the data, when sending it to the client, you only need to obtain the client's IP address and port number through requestPacket.getSocketAddress().
insert image description here
Since the server is fixed, you can set the port number directly yourself. The port number is created when the server is started.
Why do we need port numbers and IP addresses?
To achieve successful communication between the two parties, these four core indicators must be met:
Source port, source IP, destination port, destination IP
Through these four core indicators, network communication between the two parties can be achieved. There is no server IP address here because the client and server are on the same host. We can use an IP to represent 172.0.0.1, also known as the loopback IP.

1.2.4 Implementing a UDP Client

public class udpEchoClient {
    private DatagramSocket socket = null;
    private String serverIp;
    private int serverProt;
    public  udpEchoClient(String serverIp, int serverProt) throws SocketException {
        socket = new DatagramSocket();
        this.serverIp = serverIp;
        this.serverProt = serverProt;
    }
    public void start() throws IOException {
        System.out.println("客户端启动!");
        //1.从控制台获取字符串
        Scanner scanner = new Scanner(System.in);
        while(true) {
            System.out.println("请输入要发送的请求: ");
            String request = scanner.nextLine();
            //2.将输入的字符串发送给服务器,还要将发送的目标确定,IP和端口号
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), 0, request.getBytes().length,
                    InetAddress.getByName(serverIp), serverProt);
            socket.send(requestPacket);
            //3.从服务器读取到经过处理的响应
            DatagramPacket responsePackt = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePackt);
            //将响应打印在控制台
            String response = new String(responsePackt.getData(), 0, responsePackt.getLength());
            System.out.println(response);
        }
    }
    public static void main(String[] args) throws IOException {
        udpEchoClient udpEchoClient = new udpEchoClient("127.0.0.1",9999);
        udpEchoClient.start();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

insert image description here
The requestPacket object obtains the reference and length of the string received from the console, as well as its own IP and Prot, and then sends it to the server through the socket's send method.
insert image description here
To receive the response from the server, you need to create a responsePackt object and create a blank byte array in advance to receive the response received from the server.
Let’s distinguish some methods:
Before distinguishing the methods, we need to understand the concepts of bytes, characters, and encoding forms.
Bytes and characters are essentially binary, which is data like 0101, because computers only recognize 0101.
Byte: Byte is the basic unit of computer data storage. 1 byte is 8 bits, each bit can be 0 or 1, which means that a byte can represent 256 different values.
Character: Character is the basic unit for representing text information. Depending on the encoding format, the number of bytes corresponding to one character will vary. Through the encoding format, characters can be converted into bytes and then bytes can be used to represent, transmit and store data.
Encoding format:

  1. ASCII code: used to represent English characters. It uses 7 bits to represent 128 characters (including uppercase and lowercase letters, numbers, punctuation marks, and some control characters), but does not support Chinese characters. Here, one character corresponds to one byte.
  2. Unicode (UTF8): UTF-8 is a variable-length encoding form that includes characters from all writing systems in the world. It is compatible with the ASCII character set and supports Chinese characters, including characters from all writing systems in the world. One character corresponds to 1-4 bytes.
  3. GBK: uses variable-length encoding, mainly used for Chinese character encoding, one character corresponds to 1-2 bytes

The method is different:

  1. getBytes(): Get a reference to a byte array. The object is a character. Here, the characters are converted into bytes to store data.
  2. getBytes().length: Get the length of the byte array, the object is a character
  3. getData(): Gets a reference to a string of characters. The object is bytes. Here, bytes are converted into characters to use data.
  4. getLength(): Get the length of the string, the object is bytes
  5. getAddress(): Get the IP address, the object is DatagramPacket
  6. getPort(): Get the Prot port number, the object is DatagramPacket
  7. getSocketAddress(): Get the IP address and Port number, the object is DatagramPacket
  8. InetAddress.getByName(): This method can resolve the host name and return the IP address of the host name, or directly return the InetAddress object of the given IP address. The obtained InetAddress object is passed to the server, and the server can resolve the IP address by obtaining this object.

1.2.5 Implementing a UDP dictionary translation server

Here we can apply the idea of ​​inheritance and polymorphism to implement this server, because the essential difference between this dictionary translation server and the echo server is the processing of responses, so we can inherit and reuse the repeated code and expand the required code.

public class udpDictServer extends udpEchoServer{
    HashMap<String,String> map = null;
    public udpDictServer(int port) throws SocketException {
        super(port);
        map = new HashMap<>();
        map.put("server","服务");
        map.put("client","客户端");
        map.put("cat","🐱");
        map.put("dog","🐕");
        map.put("pig","🐖");
    }
    @Override
    public String process(String request) {
        return map.getOrDefault(request, "该词汇没有查询到");
    }

    public static void main(String[] args) throws IOException {
        udpDictServer udpDictServer = new udpDictServer(9999);
        udpDictServer.start();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

To implement this dictionary translation, we also need a map in the data structure and use the key-value pairs in the map to store data.
insert image description here
Then the specific business operations only need to rewrite the process method.
insert image description here
When executing here, although this refers to the udpEchoServer object of the parent class, the udpDictServer subclass inherits the udpEchoServer parent class and has rewritten the process method. Due to the polymorphic nature, the process method executed is actually the process method of the subclass.

2. Client and server processes

  1. Get data from the console
  2. The client sends data to the server
  3. The server receives data sent by the client
  4. Calculate the response
  5. The server sends the calculated response to the client
  6. The client receives the response from the server
  7. The response received from the client and sent by the server is printed in the console