[Programmation du réseau socket] [2] Démarrage rapide UDP

1. Un protocole de datagramme, non orienté connexion. Une fois qu'il envoie les données envoyées par l'application à la couche réseau, il ne conserve pas de sauvegarde des données.

Applicable à: DNS, TFTP, SNMP, transmission en temps réel

2. La longueur maximale d'un paquet UDP est de 65507 octets

 

3. Introduction à l'API principale d'UDP

(1) DatagramSocket: une classe pour recevoir et envoyer des paquets UDP

DatagramSocket (): Créez une instance simple sans spécifier le port et l'adresse IP. Si vous utilisez cet exemple pour envoyer des paquets UDP, il réutilisera le port disponible localement, ip est l'IP de la machine.

DatagramSocket (port int): crée une instance d'écoute sur un port fixe. Indique que le message de réponse peut être reçu via ce port

DatagramSocket (int port, InetAddress localAddr): crée une instance d'un port fixe et d'une adresse IP

receive (DatagramPacket d): reçoit le paquet de données UDP. DatagramPacket, comme son nom l'indique, est une classe d'encapsulation de paquets de données UDP

send (DatagramPacket d): envoyer un paquet de données UDP

setSoTimeout (int timeuot): définir les heures supplémentaires, unité: milliseconde

close (): ferme et libère les ressources

(2) DatagramPacket: classe d'encapsulation du paquet de données UDP, qui est la classe d'entité pour l'envoi et la réception UDP. En même temps, il dispose de certaines méthodes pour encapsuler des données telles que le tableau d'octets, l'adresse cible et le port cible dans les données UDP paquets ou paquets de données UDP désassemblés en tableau d'octets

DatagramPacket (byte [] buf, int offset, int length, InetAddress address, in port): offset est l'index de début de la partie utilisée du tableau d'octets. Les 3 premiers paramètres spécifient la plage d'utilisation de buf. Les deux derniers paramètres spécifient l'ip et le port de la machine cible, qui ne sont valides que lors de l'envoi , mais invalides lors de la réception

DatagramPacket (byte [] buf, int offset, SocketAddress address): SocketAddress est équivalent au package de InetAddress + port

setData (byte [] buf, int offset, int length): méthode de compression des données en données UDP

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

setAddress (adresse InetAddress) 、 setPort (port int) 、 setSocketAddress ()

 

4. Unicast UDP, diffusion, multidiffusion (ou appelé: multidiffusion)

 

5. Calcul et compréhension de l'adresse de diffusion (besoin de comprendre le contenu de la division des sous-réseaux et de la construction de supernets dans le cours de réseau informatique, sautez-le si vous ne l'avez pas appris)

Un connu

ip : 192.168.124.7

Masque de sous-réseau: 255.255.255.192

(Ou écrit comme: 192.168.124.7 / 26)

Trouvez l'adresse réseau et l'adresse de diffusion.

Solution: adresse réseau = IP et masque de sous-réseau

Puisque les 3 premiers octets sont 255, les 3 premiers octets de ip sont égaux à lui-même. Donc, le quatrième octet est compté comme suit:

7 = 00000111

192 = 11000000

7 et 192 = 0

Donc, l'adresse réseau: 192.168.124.0

Il s'agit d'une adresse réseau de classe C, mais son masque de sous-réseau n'est pas le 255.255.255.0 par défaut, mais 255.255.255.192, converti en binaire, cela signifie que le numéro d'hôte a emprunté 2 bits comme numéro de sous-réseau. Ainsi, les 24 premiers chiffres sont le numéro de réseau, les 2 chiffres du milieu sont le numéro de sous-réseau et les 6 derniers chiffres sont le numéro d'hôte.

Par conséquent, ce réseau de type C peut être divisé en 2 ^ 2 = 4 sous-réseaux au maximum. Pour ces 4 sous-réseaux, les adresses IP disponibles sont (en omettant les 3 premiers octets): 0 ~ 63, 64 ~ 127, 128 ~ 191, 192 ~ 255

De toute évidence, l'ip de 192.168.124.7 se trouve dans le premier segment de sous-réseau et son adresse de diffusion est: 192.168.124.63 (c'est-à-dire la plus grande adresse IP du premier sous-réseau)

Remarque: les hôtes de différents segments de sous-réseau ne peuvent pas se diffuser entre eux. La raison est en fait très simple, car les adresses de diffusion des hôtes dans différents sous-réseaux sont différentes, elles sont donc naturellement diffusées les unes aux autres.

 

6. Sur IDEA, pratique de cas: cas de recherche LAN

(1) UDP reçoit les messages et les renvoie (simulant la monodiffusion UDP)

(2) Transmission de diffusion LAN UDP (diffusion UDP analogique)

 

7. Code

(1) UDP reçoit les messages et les renvoie (simulant la monodiffusion UDP)

Démarrez d'abord UDPProvider, puis 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) Transmission de diffusion LAN UDP (diffusion UDP analogique)

Démarrez d'abord UDPProvider (vous pouvez en démarrer plusieurs), puis démarrez 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;
        }
    }
}

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

Je suppose que tu aimes

Origine blog.csdn.net/qq_43290318/article/details/106796367
conseillé
Classement