Compartir tecnología

【JavaEE】Programación de red——UDP

2024-07-12

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

Insertar descripción de la imagen aquí
🤡🤡🤡个人主页🤡🤡🤡
🤡🤡🤡JavaEE专栏🤡🤡🤡

1. Conector de datagrama (UDP)

1.1 Características

  1. Sin conexión: no es necesario establecer un canal dedicado antes de transmitir los datos. Cada paquete de datos se envía de forma independiente y elige su propia ruta para llegar al destino. Al igual que al enviar información, usted solo necesita enviar la información y la otra parte no. necesita recibirlo.
  2. Transmisión no confiable: al transmitir datos, no es necesario prestar atención a si la otra parte los ha recibido, simplemente envíelos.
  3. Orientado al flujo de datos: La unidad básica de transmisión de datos es cada datagrama UDP. Una vez leído y escrito, solo se puede leer y escribir un datagrama UDP completo.
  4. Full-duplex: la comunicación bidireccional es posible en un enlace.

1.2 Codificación

Todas las API de Socket son proporcionadas por el sistema. Las API proporcionadas por diferentes sistemas son diferentes, pero estas API del sistema están encapsuladas aún más en Java.
La API de socket en UDP se centra en dos clases: DatagramSocket y DatagramPacket

1.2.1 Datagrama Socket

La función de esta clase puede considerarse como un control remoto para "operar la tarjeta de red", es decir, operaciones de lectura y escritura en la tarjeta de red, que puede entenderse como leer y escribir archivos similares.
Se proporcionan varios métodos:
Insertar descripción de la imagen aquí

1.2.2 Paquete de datagramas

Esta clase describe datagramas UDP. Un objeto DatagramPacket es equivalente a un datagrama UDP. Una vez enviado y recibido, se transmite un objeto DatagramPacket.

1.2.3 Implementar un servidor de eco UDP (24*7)

Eco: el cliente envía diferentes solicitudes al servidor y el servidor devuelve diferentes respuestas. Pero el eco aquí es lo que el cliente solicita al servidor, y el servidor responde sin ningún cálculo o lógica empresarial.

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

Insertar descripción de la imagen aquí
El objeto DatagramPacket representa un datagrama. Un datagrama UDP se compone de un encabezado y una carga útil. La dirección IP y el número de puerto en el encabezado son atributos de la clase DatagramPacket, pero no se proporciona la clase de carga útil, por lo que el programador debe hacerlo. Él mismo siempre, así como un monje que pide limosna, debe proporcionar un cuenco para la limosna y luego cada familia pone comida en el cuenco. Dado que la esencia del datagrama son datos binarios, proporcioné una matriz de bytes como carga útil. . Almacenamiento de datos.
Insertar descripción de la imagen aquí
Los datos recibidos del cliente deben devolverse al cliente después de ser procesados ​​por el servidor, por lo que se debe crear un objeto DatagramPacket para almacenar los datos procesados ​​por el servidor y luego el servidor los envía al cliente. En este momento, el objeto DatagramPacket no necesita proporcionar un espacio en blanco. Simplemente coloque los datos procesados ​​en este objeto. Dado que la dirección IP y el número de puerto del cliente se registran al recibir los datos, solo la dirección IP y el número de puerto del cliente. se registran al enviarlo al cliente. Debe obtener la dirección IP y el número de puerto del cliente a través de requestPacket.getSocketAddress().
Insertar descripción de la imagen aquí
Dado que el servidor es fijo, usted mismo puede configurar directamente el número de puerto. El número de puerto se crea cuando se inicia el servidor.
¿Por qué necesito una dirección IP de número de puerto?
Para lograr una comunicación exitosa entre las dos partes, estos cuatro indicadores centrales deben estar presentes:
Puerto de origen, IP de origen, puerto de destino, IP de destino
A través de estos cuatro indicadores centrales, se puede lograr la comunicación de red entre las dos partes. Aquí no hay una dirección IP del servidor porque el cliente y el servidor están en el mismo host. Podemos usar una IP para representar 172.0.0.1, también conocida como IP de loopback.

1.2.4 Implementar un cliente UDP

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

Insertar descripción de la imagen aquí
Para la cadena recibida de la consola, el objeto requestPacket obtiene la referencia y la longitud del objeto de cadena, así como su propia IP y Prot, y luego lo envía al servidor a través del método de envío del socket.
Insertar descripción de la imagen aquí
Para recibir una respuesta del servidor, debe crear un objeto ResponsePackt y crear una matriz de bytes en blanco por adelantado para recibir la respuesta del servidor.
Distinguir algunos métodos:
Antes de distinguir métodos, debemos comprender los conceptos de bytes, caracteres y formas de codificación.
Los bytes y caracteres son esencialmente binarios, que son datos como 0101, porque la computadora solo reconoce 0101.
Byte: El byte es la unidad básica para el almacenamiento de datos en una computadora. 1 byte son 8 bits. Cada bit puede ser 0 o 1, lo que significa que un byte puede representar 256 valores diferentes.
Carácter: el carácter es la unidad básica para representar información de texto. Según las diferentes formas de codificación, el número de bytes correspondientes a un carácter varía. A través del formulario de codificación, los caracteres se pueden convertir en bytes y luego los bytes se pueden usar para representar datos. datos y almacenar datos.
Forma de codificación:

  1. Código ASCII: utilizado para representar caracteres en inglés.Utiliza 7 bits para representar 128 caracteres (incluidas letras mayúsculas y minúsculas, números, signos de puntuación y algunos caracteres de control), pero no admite caracteres chinos. Aquí, un carácter corresponde a un byte.
  2. Unicode (UTF8): UTF-8 es una forma de codificación de longitud variable que incluye caracteres de todos los sistemas de escritura del mundo. Es compatible con el conjunto de caracteres ASCII y admite caracteres chinos, incluidos caracteres de todos los sistemas de escritura del mundo. El carácter corresponde a 1-4 caracteres Festival.
  3. GBK: utiliza codificación de longitud variable, que se utiliza principalmente para la codificación de caracteres chinos, un carácter corresponde a 1-2 bytes

Los métodos son diferentes:

  1. getBytes(): obtiene una referencia a la matriz de bytes. El objeto es un carácter. Aquí, los caracteres se convierten en bytes para almacenar los datos.
  2. getBytes().length: obtiene la longitud de la matriz de bytes, el objeto es un carácter
  3. getData(): Lo que se obtiene es una referencia a una cadena compuesta de caracteres. El objeto son bytes. Aquí los bytes se convierten en caracteres para utilizar los datos.
  4. getLength(): la longitud de la cadena obtenida, el objeto es bytes
  5. getAddress(): obtiene la dirección IP, el objeto es DatagramPacket
  6. getPort(): obtiene el número de puerto de Prot, el objeto es DatagramPacket
  7. getSocketAddress(): obtiene la dirección IP y el número de puerto Prot, el objeto es DatagramPacket
  8. InetAddress.getByName (): este método puede analizar el nombre de host y devolver la dirección IP del nombre de host, o devolver directamente el objeto InetAddress de la dirección IP dada. El objeto InetAddress obtenido se pasa al servidor y el servidor puede resolverlo. la IP obteniendo esta dirección de objeto.

1.2.5 Implementar un servidor de traducción de diccionarios UDP

Aquí podemos aplicar las ideas de herencia y polimorfismo para implementar este servidor, porque la diferencia esencial entre este servidor de traducción de diccionario y el servidor de eco es el procesamiento de respuestas, por lo que podemos heredar y reutilizar el código repetido y expandir el código requerido.

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

Para implementar esta traducción de diccionario, también necesita un mapa en la estructura de datos y utilizar pares clave-valor en el mapa para almacenar datos.
Insertar descripción de la imagen aquí
Entonces, las operaciones comerciales específicas solo necesitan reescribir el método del proceso.
Insertar descripción de la imagen aquí
Al ejecutar aquí, aunque esto se refiere al objeto udpEchoServer de la clase principal, debido a que la subclase udpDictServer hereda la clase principal udpEchoServer y anula el método de proceso, el método de proceso ejecutado debido a la característica polimórfica es en realidad el proceso de la subclase.

2. Proceso cliente y servidor

  1. Obtener datos de la consola
  2. El cliente envía datos al servidor.
  3. El servidor recibe los datos enviados por el cliente.
  4. Calcular respuesta
  5. El servidor envía la respuesta calculada al cliente.
  6. El cliente recibe la respuesta enviada por el servidor.
  7. Imprime la respuesta enviada desde el servidor recibida del cliente a la consola