[ソケットネットワークプログラミング] [2] UDPクイックスタート

1.コネクション型ではなく、データグラムプロトコル。アプリケーションから送信されたデータをネットワーク層に送信すると、データのバックアップは保持されません。

対象:DNS、TFTP、SNMP、リアルタイム送信

2.UDPパケットの最大長は65507バイトです

 

3.UDPのコアAPIの概要

(1)DatagramSocket:UDPパケットを送受信するためのクラス

DatagramSocket():ポートとIPを指定せずに単純なインスタンスを作成します。この例を使用してUDPパケットを送信すると、ローカルで使用可能なポートが再利用されます。ipはマシンのIPです。

DatagramSocket(int port):固定ポートでリッスンするインスタンスを作成します。このポートを介して応答メッセージを受信できることを示します

DatagramSocket(int port、InetAddress localAddr):固定ポートとIPのインスタンスを作成します

receive(DatagramPacket d):UDPデータパケットを受信します。DatagramPacketは、その名前が示すように、UDPデータパケットのカプセル化クラスです。

send(DatagramPacket d):UDPデータパケットを送信します

setSoTimeout(int timeuot):残業を設定します。単位:ミリ秒

close():リソースを閉じて解放します

(2)DatagramPacket:UDP送受信のエンティティクラスであるUDPデータパケットのカプセル化クラスであると同時に、バイト配列、ターゲットアドレス、ターゲットポートなどのデータをUDPデータにカプセル化するメソッドがいくつかあります。パケット、またはUDPデータパケットはバイト配列に分解されます

DatagramPacket(byte [] buf、int offset、int length、InetAddress address、in port):offsetは、バイト配列の使用されている部分の開始インデックスです。最初の3つのパラメーターは、bufの使用範囲を指定します。後者の2つのパラメーターは、ターゲットマシンのIPとポートを指定します。これらは送信時にのみ有効ですが、受信時には無効です。

DatagramPacket(byte [] buf、int offset、SocketAddress address):SocketAddressは、InetAddress +ポートのパッケージと同等です。

setData(byte [] buf、int offset、int length):データをUDPデータにパックする方法

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

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

 

4. UDPユニキャスト、ブロードキャスト、マルチキャスト(または呼ばれる:マルチキャスト)

 

5.ブロードキャストアドレスの計算と理解(コンピュータネットワークコースでのサブネットの分割とスーパーネットの構築の内容を理解する必要があります。学習していない場合はスキップしてください)

既知の

ip:192.168.124.7

サブネットマスク:255.255.255.192

(または次のように記述されます:192.168.124.7 / 26)

ネットワークアドレスとブロードキャストアドレスを見つけます。

解決策:ネットワークアドレス= IP&サブネットマスク

最初の3バイトは255であるため、ipの最初の3バイトはそれ自体と同じです。したがって、4番目のバイトは次のようにカウントされます。

7 = 00000111

192 = 11000000

7&192 = 0

したがって、ネットワークアドレス:192.168.124.0

これはクラスCネットワークアドレスですが、そのサブネットマスクはデフォルトの255.255.255.0ではなく、255.255.255.192であり、バイナリに変換されます。これは、ホスト番号がサブネット番号として2ビットを借用したことを意味します。したがって、最初の24桁はネットワーク番号、中央の2桁はサブネット番号、最後の6桁はホスト番号です。

したがって、このタイプCネットワークは最大で2 ^ 2 = 4のサブネットに分割できます。これらの4つのサブネットの場合、使用可能なIPアドレスは(最初の3バイトを省略):0〜63、64〜127、128〜191、192〜255です。

明らかに、192.168.124.7のIPは最初のサブネットセグメントにあり、そのブロードキャストアドレスは192.168.124.63(つまり、最初のサブネットの最大IPアドレス)です。

注:異なるサブネットセグメント内のホストは、相互にブロードキャストできません。異なるサブネット内のホストのブロードキャストアドレスが異なるため、理由は実際には非常に単純です。したがって、それらは自然に相互にブロードキャストされます。

 

6. IDEAでは、ケースプラクティス:LAN検索ケース

(1)UDPはメッセージを受信して​​送り返します(UDPユニキャストをシミュレート)

(2)UDP LANブロードキャスト送信(アナログUDPブロードキャスト)

 

7.コード

(1)UDPはメッセージを受信して​​送り返します(UDPユニキャストをシミュレート)

最初にUDPProviderを起動し、次に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)UDP LANブロードキャスト送信(アナログUDPブロードキャスト)

最初にUDPProviderを起動し(複数起動できます)、次に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;
        }
    }
}

CountDownLatchについて:https//www.iteye.com/blog/zapldy-746458

おすすめ

転載: blog.csdn.net/qq_43290318/article/details/106796367