[Socket network programming] [2] UDP quick start

1. A datagram protocol, not connection-oriented. Once it sends out the data sent by the application to the network layer, it does not keep a backup of the data.

Applicable to: DNS, TFTP, SNMP, real-time transmission

2. The maximum length of a UDP packet is 65507 bytes

 

3. Introduction to the core API of UDP

(1) DatagramSocket: a class for receiving and sending UDP packets

DatagramSocket(): Create a simple instance without specifying the port and ip. If you use this example to send UDP packets, it will reuse the locally available port, ip is the ip of the machine.

DatagramSocket(int port): Create an instance of listening on a fixed port. Indicates that the reply message can be received through this port

DatagramSocket(int port, InetAddress localAddr): create an instance of a fixed port and ip

receive(DatagramPacket d): Receive UDP data packet. DatagramPacket, as the name implies, is an encapsulation class of UDP data packets

send(DatagramPacket d): send UDP data packet

setSoTimeout(int timeuot): Set the overtime, unit: millisecond

close(): close and release resources

(2) DatagramPacket: An encapsulation class of UDP data packet, which is the entity class for UDP sending and receiving. At the same time, it has some methods to encapsulate data such as byte array, target address, and target port into UDP data packets, or UDP data Packet disassembled into byte array

DatagramPacket(byte[] buf, int offset, int length, InetAddress address, in port): offset is the start index of the used part of the byte array. The first 3 parameters specify the use range of buf. The latter two parameters specify the ip and port of the target machine, which are only valid when sending , but invalid when receiving

DatagramPacket(byte[] buf, int offset, SocketAddress address): SocketAddress is equivalent to the package of InetAddress + port

setData(byte[] buf, int offset, int length): method of packing data to UDP data

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

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

 

4. UDP unicast, broadcast, multicast (or called: multicast)

 

5. Calculation and understanding of broadcast address (need to understand the content of dividing subnets and constructing supernets in the computer network course, skip it if you have not learned it)

A known

ip:192.168.124.7

Subnet mask: 255.255.255.192

(Or written as: 192.168.124.7 / 26)

Find the network address and broadcast address.

Solution: network address = ip & subnet mask

Since the first 3 bytes are 255, the first 3 bytes of ip are equal to itself. So the fourth byte is counted as follows:

7      =  00000111

192  =  11000000

7 & 192 = 0

So the network address: 192.168.124.0

This is a Class C network address, but its subnet mask is not the default 255.255.255.0, but 255.255.255.192, converted to binary, it means that the host number has borrowed 2 bits as the subnet number. So the first 24 digits are the network number, the middle 2 digits are the subnet number, and the last 6 digits are the host number.

Therefore, this type C network can be divided into 2^2=4 subnets at most. For these 4 subnets, the available ip addresses are (omitting the first 3 bytes): 0~63, 64~127, 128~191, 192~255

Obviously, the ip of 192.168.124.7 is in the first subnet segment, and its broadcast address is: 192.168.124.63 (that is, the largest ip address of the first subnet)

Note: Hosts in different subnet segments cannot broadcast to each other. The reason is actually very simple, because the broadcast addresses of hosts in different subnets are different, so they are naturally broadcast to each other.

 

6. On IDEA, case practice: LAN search case

(1) UDP receives messages and sends them back (simulating UDP unicast)

(2) UDP LAN broadcast transmission (analog UDP broadcast)

 

7. Code

(1) UDP receives messages and sends them back (simulating UDP unicast)

Start UDPProvider first, then 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 broadcast transmission (analog UDP broadcast)

Start UDPProvider first (you can start multiple), then start 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;
        }
    }
}

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

Guess you like

Origin blog.csdn.net/qq_43290318/article/details/106796367