欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

【Socket网络编程】【2】UDP快速入门

程序员文章站 2022-06-22 17:26:39
1、一种数据报协议,并非面向连接。它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份。可应用于:DNS, TFTP, SNMP,实时传输2、UDP包的最大长度是65507 字节3、UDP的核心API介绍(1)DatagramSocket:用于接收与发送UDP包的类DatagramSocket():创建简单实例,不指定端口和ip。如果使用这个实例来发送UDP包,它会复用本地可用的端口,ip是本机的ip。DatagramSocket(int port):创建监听固定端口的实....

1、一种数据报协议,并非面向连接。它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份。

可应用于:DNS, TFTP, SNMP,实时传输

2、UDP包的最大长度是65507 字节

 

3、UDP的核心API介绍

(1)DatagramSocket:用于接收与发送UDP包的类

DatagramSocket():创建简单实例,不指定端口和ip。如果使用这个实例来发送UDP包,它会复用本地可用的端口,ip是本机的ip。

DatagramSocket(int port):创建监听固定端口的实例。表示可以通过这个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发送和接收的实体类,同时它具备一些方法,能够将byte数组、目标地址、目标端口等数据 封装成UDP数据包,或者将UDP数据包拆卸成byte数组

DatagramPacket(byte[] buf, int offset, int length, InetAddress address, in port):offset是byte数组的使用用部分的起始索引。前面3个参数指定的是buf的使用区间。后面两个参数指定的是目标机器的ip和端口,仅仅是发送时有效,接收时是无效的

DatagramPacket(byte[] buf, int offset, SocketAddress address):SocketAddress相当于就是InetAddress + port的封装

setData(byte[] buf, int offset, int length):往UDP数据包装数据的方法

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

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

 

4、UDP 单播、广播、多播(或称为:组播)

【Socket网络编程】【2】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上,案例实操:局域网搜索案例

(1)UDP接收消息并回送(模拟UDP单播)

(2)UDP局域网广播发送(模拟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局域网广播发送(模拟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

本文地址:https://blog.csdn.net/qq_43290318/article/details/106796367