[Programación de la red de enchufes] [2] Inicio rápido de UDP

1. Un protocolo de datagrama, no orientado a la conexión. Una vez que envía los datos enviados por la aplicación a la capa de red, no guarda una copia de seguridad de los datos.

Aplicable a: DNS, TFTP, SNMP, transmisión en tiempo real

2. La longitud máxima de un paquete UDP es 65507 bytes.

 

3. Introducción a la API principal de UDP

(1) DatagramSocket: una clase para recibir y enviar paquetes UDP

DatagramSocket (): crea una instancia simple sin especificar el puerto y la ip. Si usa este ejemplo para enviar paquetes UDP, reutilizará el puerto disponible localmente, ip es la ip de la máquina.

DatagramSocket (puerto int): crea una instancia de escucha en un puerto fijo. Indica que el mensaje de respuesta se puede recibir a través de este puerto

DatagramSocket (int port, InetAddress localAddr): crea una instancia de un puerto fijo y una ip

recibir (DatagramPacket d): recibe el paquete de datos UDP. DatagramPacket, como su nombre lo indica, es una clase de encapsulación de paquetes de datos UDP

enviar (DatagramPacket d): enviar paquete de datos UDP

setSoTimeout (int timeuot): establece el tiempo extra, unidad: milisegundo

close (): cerrar y liberar recursos

(2) DatagramPacket: una clase de encapsulación de paquete de datos UDP, que es la clase de entidad para el envío y la recepción de UDP. Al mismo tiempo, tiene algunos métodos para encapsular datos como matriz de bytes, dirección de destino y puerto de destino en datos UDP paquetes o paquetes de datos UDP desensamblados en una matriz de bytes

DatagramPacket (byte [] buf, int offset, int length, InetAddress address, in port): offset es el índice de inicio de la parte utilizada de la matriz de bytes. Los primeros 3 parámetros especifican el rango de uso de buf. Los dos últimos parámetros especifican la ip y el puerto de la máquina de destino, que solo son válidos al enviar , pero no válidos al recibir

DatagramPacket (byte [] buf, int offset, dirección SocketAddress): SocketAddress es equivalente al paquete de InetAddress + puerto

setData (byte [] buf, int offset, int length): método de empaquetar datos en datos UDP

getData () 、 getOffset () 、 getLength () 、 getSocketAddress ()

setAddress (dirección InetAddress) 、 setPort (puerto int) 、 setSocketAddress ()

 

4. UDP unicast, broadcast, multicast (o llamado: multicast)

 

5. Cálculo y comprensión de la dirección de transmisión (es necesario comprender el contenido de la división de subredes y la construcción de superredes en el curso de redes informáticas; omítelo si no lo ha aprendido)

Un conocido

ip: 192.168.124.7

Máscara de subred: 255.255.255.192

(O escrito como: 192.168.124.7 / 26)

Busque la dirección de red y la dirección de transmisión.

Solución: dirección de red = ip y máscara de subred

Dado que los primeros 3 bytes son 255, los primeros 3 bytes de ip son iguales a sí mismo. Entonces, el cuarto byte se cuenta de la siguiente manera:

7 = 00000111

192 = 11000000

7 y 192 = 0

Entonces la dirección de red: 192.168.124.0

Esta es una dirección de red de Clase C, pero su máscara de subred no es la predeterminada 255.255.255.0, sino 255.255.255.192, convertida a binaria, significa que el número de host ha tomado prestados 2 bits como número de subred. Entonces, los primeros 24 dígitos son el número de red, los 2 dígitos del medio son el número de subred y los últimos 6 dígitos son el número de host.

Por lo tanto, esta red de tipo C se puede dividir en 2 ^ 2 = 4 subredes como máximo. Para estas 4 subredes, las direcciones IP disponibles son (omitiendo los primeros 3 bytes): 0 ~ 63, 64 ~ 127, 128 ~ 191, 192 ~ 255

Obviamente, la ip de 192.168.124.7 está en el primer segmento de subred y su dirección de transmisión es: 192.168.124.63 (es decir, la dirección IP más grande de la primera subred)

Nota: los hosts de diferentes segmentos de subred no pueden transmitirse entre sí. La razón es realmente muy simple, porque las direcciones de transmisión de los hosts en diferentes subredes son diferentes, por lo que naturalmente se transmiten entre sí.

 

6. Sobre IDEA, práctica de caso: caso de búsqueda de LAN

(1) UDP recibe mensajes y los envía de vuelta (simulando unidifusión UDP)

(2) Transmisión de transmisión UDP LAN (transmisión UDP analógica)

 

7. Código

(1) UDP recibe mensajes y los envía de vuelta (simulando unidifusión UDP)

Inicie UDPProvider primero, luego UDPSearcher

UDPProvider.java

package UDPDemo;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

/**
 * @author passerbyYSQ
 * @create 2020-06-16 23:23
 */
public class UDPProvider {
    public static void main(String[] args) throws IOException {
        System.out.println("UDPProvider started.");

        // 注意此处的port是监听本机的端口2000
        DatagramSocket ds = new DatagramSocket(20000);

        // 构建接收的实体
        final byte[] buf = new byte[512];
        DatagramPacket receivePkt = new DatagramPacket(buf, buf.length);

        // 接收。阻塞。
        ds.receive(receivePkt);

        // 从UDP数据包中获取发送者的ip和端口
        String senderIp = receivePkt.getAddress().getHostAddress();
        int port = receivePkt.getPort();
        int dataLen = receivePkt.getLength();

        String dataStr = new String(receivePkt.getData(), 0, dataLen);
        System.out.println("UDPProvider receive from ip: " + senderIp
        + "\tport: " + port + "\tdata: " + dataStr);

        // 构建一份回送的数据包
        String responseData = "Receive data with len: " + dataLen;
        byte[] responseBytes = responseData.getBytes();
        DatagramPacket responsePkt = new DatagramPacket(responseBytes,
                responseBytes.length, receivePkt.getAddress(), receivePkt.getPort());
        ds.send(responsePkt);

        // 结束
        System.out.println("UDPProvider finished.");
        ds.close();

    }
}

UDPSearcher.java

package UDPDemo;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

/**
 * @author passerbyYSQ
 * @create 2020-06-16 23:23
 */
public class UDPSearcher {
    public static void main(String[] args) throws IOException {
        System.out.println("UDPSearcher started.");

        // 让系统随机分配端口,用于发送数据包
        DatagramSocket ds = new DatagramSocket();

        // 构建一份回送的数据包
        String requestData = "Hello World";
        byte[] requestBytes = requestData.getBytes();
        DatagramPacket requestPkt = new DatagramPacket(requestBytes, requestBytes.length);
        requestPkt.setAddress(InetAddress.getLocalHost()); // 发送给本机
        requestPkt.setPort(20000); // 发送给20000

        // 发送
        ds.send(requestPkt);

        // 构建接收的实体
        final byte[] buf = new byte[512];
        DatagramPacket receivePkt = new DatagramPacket(buf, buf.length);

        // 接收
        ds.receive(receivePkt);

        // 从UDP数据包中获取发送者的ip和端口
        String senderIp = receivePkt.getAddress().getHostAddress();
        int port = receivePkt.getPort();
        int dataLen = receivePkt.getLength();

        String dataStr = new String(receivePkt.getData(), 0, dataLen);
        System.out.println("UDPSearcher receive from ip: " + senderIp
                + "\tport: " + port + "\tdata: " + dataStr);

        // 结束
        System.out.println("UDPSearcher finished.");
        ds.close();
    }
}

(2) Transmisión de transmisión UDP LAN (transmisión UDP analógica)

Inicie UDPProvider primero (puede iniciar varios), luego inicie UDPSearcher

UDPProvider.java

package UDPDemo2;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.UUID;

/**
 * 接收广播的多方(B,有多个)
 *
 * @author passerbyYSQ
 * @create 2020-06-16 23:23
 */
public class UDPProvider {
    public static void main(String[] args) throws IOException {
        // 唯一标识
        String sn = UUID.randomUUID().toString();
        Provider provider = new Provider(sn);
        provider.start();

        // 读取任意一个字符,表示退出
        System.in.read();
        provider.exit();
    }

    private static class Provider extends Thread {
        private final String sn;
        // 标记状态
        private boolean done = false;
        // 用于接收和发送UDP数据包
        private DatagramSocket ds = null;


        public Provider(String sn) {
            super();
            this.sn = sn;
        }

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

            System.out.println("UDPProvider started.");

            try {
                // 注意此处的port是监听本机的端口2000
                ds = new DatagramSocket(20000);
                while (!done) {
                    // 构建接收的实体
                    final byte[] buf = new byte[512];
                    DatagramPacket receivePkt = new DatagramPacket(buf, buf.length);

                    // 接收。阻塞。
                    ds.receive(receivePkt);

                    // 从UDP数据包中获取发送者的ip和端口
                    String senderIp = receivePkt.getAddress().getHostAddress();
                    int port = receivePkt.getPort();
                    int dataLen = receivePkt.getLength();

                    String dataStr = new String(receivePkt.getData(), 0, dataLen);
                    System.out.println("UDPProvider receive from ip: " + senderIp
                            + "\tport: " + port + "\tdata: " + dataStr);

                    // 解析端口
                    int responsePort = MessageCreator.parsePort(dataStr);
                    if (responsePort != -1) {
                        // 构建一份回送的数据包
                        String responseData = MessageCreator.buildWithSn(sn);
                        byte[] responseBytes = responseData.getBytes();
                        DatagramPacket responsePkt = new DatagramPacket(responseBytes,
                                responseBytes.length, receivePkt.getAddress(),
                                responsePort); // 注意回送到约定的端口,而不是receivePkt中的端口
                        ds.send(responsePkt);
                    }

                }
            } catch (Exception ignored) {
                //e.printStackTrace();
                // ds.receive(receivePkt); 接收处于阻塞,状态,此时ds.close会抛出异常
                // 此异常忽略,不打印
            } finally {
                close();
            }

            // 结束
            System.out.println("UDPProvider finished.");
        }

        // 停止监听。给外部调用
        void exit() {
            done = true;
            close();
        }

        private void close() {
            if (ds != null) {
                ds.close();
                ds = null;
            }
        }
    }
}

UDPSearcher.java

package UDPDemo2;

import java.io.IOException;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * 发送广播的一方(A)
 * @author passerbyYSQ
 * @create 2020-06-16 23:23
 */
public class UDPSearcher {
    private static final int LISTEN_PORT = 30000;

    public static void main(String[] args) throws IOException, InterruptedException {
        System.out.println("UDPSearcher started.");

        Listener listener = listen();
        sendBroadcast();

        System.in.read();
        List<Device> devices = listener.getDevicesAndClose();
        for (Device device : devices) {
            System.out.println("devices:" + device.toString());
        }

        System.out.println();

        System.out.println("UDPSearcher finished.");
    }

    private static Listener listen() throws InterruptedException {
        System.out.println("UDPSearcher listener started.");

        CountDownLatch countDownLatch = new CountDownLatch(1);
        Listener listener = new Listener(LISTEN_PORT, countDownLatch);
        listener.start();

        countDownLatch.await();
        return listener;
    }

    // 发送广播
    private static void sendBroadcast() throws IOException {
        System.out.println("UDPSearcher sendBroadcast started.");

        // 让系统随机分配端口,用于发送数据包
        DatagramSocket ds = new DatagramSocket();

        // 构建一份回送的数据包
        String requestData = MessageCreator.buildWithPort(LISTEN_PORT);
        byte[] requestBytes = requestData.getBytes();
        DatagramPacket requestPkt = new DatagramPacket(requestBytes, requestBytes.length);
        requestPkt.setAddress(InetAddress.getByName("255.255.255.255")); // 发送给广播地址
        requestPkt.setPort(20000); // 发送给20000

        // 发送
        ds.send(requestPkt);
        ds.close();

        // 结束
        System.out.println("UDPSearcher sendBroadcast finished.");
    }

    private static class Device {
        final int port;
        final String ip;
        final String sn; // 标识

        public Device(int port, String ip, String sn) {
            this.port = port;
            this.ip = ip;
            this.sn = sn;
        }

        @Override
        public String toString() {
            return "Device{" +
                    "port=" + port +
                    ", ip='" + ip + '\'' +
                    ", sn='" + sn + '\'' +
                    '}';
        }
    }

    private static class Listener extends Thread {

        private final int listenPort;
        private final CountDownLatch countDownLatch;
        private final List<Device> devices = new ArrayList<>();
        private boolean done = false;
        private DatagramSocket ds = null;

        public Listener(int listenPort, CountDownLatch countDownLatch) {
            super();
            this.listenPort = listenPort;
            this.countDownLatch = countDownLatch;
        }

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

            // 通知已启动
            countDownLatch.countDown();
            try {
                ds = new DatagramSocket(listenPort);
                while (!done) {
                    // 构建接收的实体
                    final byte[] buf = new byte[512];
                    DatagramPacket receivePkt = new DatagramPacket(buf, buf.length);

                    // 接收
                    ds.receive(receivePkt);

                    // 从UDP数据包中获取发送者的ip和端口
                    String senderIp = receivePkt.getAddress().getHostAddress();
                    int port = receivePkt.getPort();
                    int dataLen = receivePkt.getLength();

                    String dataStr = new String(receivePkt.getData(), 0, dataLen);
                    System.out.println("UDPSearcher receive from ip: " + senderIp
                            + "\tport: " + port + "\tdata: " + dataStr);

                    // 解析
                    String sn = MessageCreator.parseSn(dataStr);
                    if (sn != null) {
                        Device device = new Device(port, senderIp, sn);
                        devices.add(device);
                    }
                }
            } catch (Exception ignored) {
                //e.printStackTrace();
            } finally {
                close();
            }

            // 结束
            System.out.println("UDPSearcher listener finished.");
        }

        private void close() {
            if (ds != null) {
                ds.close();
                ds = null;
            }
        }

        List<Device> getDevicesAndClose() {
            done = true;
            close();
            return devices;
        }
    }
}

Acerca de CountDownLatch: https://www.iteye.com/blog/zapldy-746458

Supongo que te gusta

Origin blog.csdn.net/qq_43290318/article/details/106796367
Recomendado
Clasificación