UDP之广播搜索局域网内设备信息

UDP是什么?

它是一种用户数据报协议,又称用户数据报文协议,是一个简单的面向数据报的传输层协议,正式规范为RFC 768;UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据报的方法

UDP核心API

  • DatagramSocket:用于接收与发送UDP信息的类(发送/接收UDP包),不同于TCP, UDP没有合并到Socket api中, 该类即是服务器也是客户端.
  • DatagramPacket:即UDP报文封装类,用于处理报文, 将byte数组、目标地址、目标端口等数据包装成报文或将报文解为byte数组,它是UDP的发送实体和接收实体。
    至于目标地址、目标端口的具体含义取决于该数据报文是被发送还是被接收。若是要发送的数据报文,则表示接收端的ip和端口;若是接收到的数据报文,则表示发送端的ip和端口。

DatagramSocket构造方法介绍

  • DatagramSocket():创建简单实例,不指定端口与IP,让系统自动分配
  • DatagramSocket(int port):创建监听固定端口的实例
  • DatagramSocket(int port,InetAddress localAddr):创建固定端口指定IP的实例

DatagramSocket常用方法

  • receive(DatagramPacket d):接收一个数据报文
  • send(DatagramPacket d):发送一个数据报文
  • setSoTimeout(int timeout):设置超时时间,单位毫秒
  • close():关闭、释放资源

DatagramPacket的构造方法

  • DatagramPacket(byte buf[], int length, InetAddress address, int port):通常用于发送数据报文时使用,前2个参数指定buf的使用区间,后面2个参数指定目标机器的地址与端口.
  • DatagramPacket(byte[]buf,int offset, int length,InetAddress address,int port):通常用于发送数据报文时使用,前3个参数指定buf的使用区间,后面2个参数指定目标机器的地址与端口.
  • DatagramPacket(byte[buf,int length,SocketAddress address]):通常用于发送数据报文时使用,前2个参数指定buf的使用区间,SocketAddress 相当于InetAddress+Port.
  • DatagramPacket(byte buf[], int length) :通常用于接收数据报文时使用,参数用于指定buf和buf的长度.

DatagramPacket的常用方法

  • setData(byte[]buf,int offset,int length):设置指定长度的数据
  • setData(byte[]buf):设置buf的完整数据
  • setLength(int length):设置buf数据的长度
  • getData():获取报文中的数据
  • getOffset():获取报文中数据buf的偏移量
  • getLength():获取报文中数据buf的长度
  • setAddress(InetAddress iaddr):设置目标地址(发送端用于设置数据报的接收端地址)
  • setPort(int port):设置目标端口(发送端用于设置数据报的接收端端口)
  • getAddress():获取目标地址(发送端地址)
  • getPort():获取目标端口(发送端端口)
  • setSocketAddress(SocketAddress adress):设置目标地址+端口
  • getSocketAddress():获取目标地址+端口

通常get方法用于获取该数据报的发送者信息,而set方法用于设置数据报的接收端信息,当然如果你使用多参数的构造方法的话,可以减少set方法的使用。

单播、广播、多播

  • 单播:就是点对点发送信息,不被其他点所感知。
  • 广播:给所有端发送信息,会受到路由器的隔离限制,仅仅在同一局域网内有效。
  • 多播:也称为组播,是向一组点发送信息。

案例介绍

该案例分为UDP提供者(即被搜索的设备,例如局域网中的智能设备)和UDP搜索者(例如局域网中的手机)。

需要完成的功能是通过手机查找局域网中智能设备的唯一标识,假设唯一标识是一串UUID值,查找后打印出来。

UDP提供者需要实现的功能是循环的监听特定端口,然后解析收到的数据,判断该数据是否符合预定的格式,从中获取到发送者的响应端口,并将唯一标识UUID值响应给UDP的搜索者。

UDP搜索者需要实现的功能就是监听特定端口和发送局域网广播,发送广播的时候将监听端口设置在数据中,因此需要先开启监听完成后,才能发送广播,一旦收到响应数据,就可以解析设备信息了。

下面开始编码,首先来一个MessageCreator类,用于封装和解析端口以及设备唯一标识

/**
 * 消息创建者
 */
public class MessageCreator {
    private static final String SN_HEADER = "Receive Port I'm UDPProvider (SN):"; //UDPProvider 回复的sn
    private static final String PORT_HEADER = "I'm UDPSearcher,Please send to (Port):"; //UDPSearcher的响应端口

    public static String buildWithPort(int port) {
        return PORT_HEADER + port;
    }

    public static int parsePort(String data){
        if (data.startsWith(PORT_HEADER)) {
            return Integer.parseInt(data.substring(PORT_HEADER.length()));
        }
        return -1;
    }

    public static String buildWithSn(String sn) {
        return SN_HEADER + sn;
    }

    public static String parseSn(String data){
        if (data.startsWith(SN_HEADER)) {
            return data.substring(SN_HEADER.length());
        }
        return null;
    }
}

然后是UDPProvider类

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

/**
 * UDP 提供者,用于提供服务
 */
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;
        private DatagramSocket ds = null;

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

        @Override
        public void run() {
            super.run();
            System.out.println("UDPProvider Started");
            try {
                // 作为接收者,指定一个端口用于数据接收
                ds = new DatagramSocket(20000);
                while (!done) {

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

                    //开始接收
                    ds.receive(receivePack);

                    //打印接收到的信息与发送者的信息
                    String ip = receivePack.getAddress().getHostAddress();//发送者的ip
                    int port = receivePack.getPort();//发送者的端口
                    int dataLen = receivePack.getLength();//数据包长度
                    String data = new String(receivePack.getData(), 0, dataLen);//数据内容

                    //打印发送者ip/端口/数据
                    System.out.println("UDPProvider receive from ip:" + ip + "\tport:" + port + "\tdata:" + data);

                    //解析响应端口号
                    int responsePort = MessageCreator.parsePort(data);
                    if (responsePort != -1) {
                        //构建一份回送数据将设备的sn返回给搜索者
                        String responseData = MessageCreator.buildWithSn(sn);
                        byte[] responseDataBytes = responseData.getBytes();
                        //直接根据发送者构建一份回送信息
                        DatagramPacket responsePacket = new DatagramPacket(responseDataBytes,
                                responseDataBytes.length,
                                receivePack.getAddress(),//发送者的ip拿来指定为接收端ip
                                responsePort); //回送到指定端口
                        ds.send(responsePacket);
                    }

                }
            } catch (Exception ignored) {
            } finally {
                close();
            }
            //完成
            System.out.println("UDPProvider Finished");
        }

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

        /**
         * 结束
         */
        void exit() {
            done = true;
            close();
        }
    }
}

最后是UDPSearcher类

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

/**
 * UDP 搜索者
 */
public class UDPSearcher {
    //监听回送端口号
    private static final int LISTENER_PORT = 30000;

    public static void main(String[] args) throws IOException, InterruptedException {
        System.out.println("UDPSearcher  Started");
        //开启监听30000端口
        Listener listener = listen();
        //发送广播
        sendBroadcast();

        //读取任意信息结束
        System.in.read();

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

    /**
     * 监听UDP提供者的回应
     */
    private static Listener listen() throws InterruptedException {
        System.out.println("UDPSearcher  start listen");
        CountDownLatch countDownLatch = new CountDownLatch(1);
        Listener listener = new Listener(LISTENER_PORT, countDownLatch);
        listener.start();
        countDownLatch.await(); //等待监听启动完成,这里是阻塞的

        return listener;//启动完成就返回监听
    }

    /**
     * 发送广播开始搜索UDP提供者
     *
     * @throws IOException
     */
    private static void sendBroadcast() throws IOException {
        System.out.println("UDPSearcher sendBroadcast Started");

        // 作为搜索方,无需指定端口,让系统自动分配
        DatagramSocket ds = new DatagramSocket();

        //发送一份请求数据,暴露监听端口
        String requestData = MessageCreator.buildWithPort(LISTENER_PORT);
        byte[] requestDataBytes = requestData.getBytes();
        //创建一个DatagramPacket
        DatagramPacket requestPacket = new DatagramPacket(requestDataBytes,
                requestDataBytes.length);
        //指定接收方的ip地址,由于用的是本机接收,所以可以用localhost
        //requestPacket.setAddress(InetAddress.getLocalHost());
        //或者指定接收端具体ip也可以,这里用的是受限的广播地址
        //requestPacket.setAddress(InetAddress.getByName("255.255.255.255"));
        //给特定网段广播地址发送
        requestPacket.setAddress(InetAddress.getByName("192.168.1.255"));

        //指定接收方的端口号
        requestPacket.setPort(20000);
        //开始发送广播
        ds.send(requestPacket);
        ds.close();
        //发送广播完成
        System.out.println("UDPSearcher sendBroadcast Finished");
    }

    /**
     * 目标设备(UDP提供者)
     */
    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 receivePack = new DatagramPacket(buf, buf.length);

                    //开始接收
                    ds.receive(receivePack);

                    //打印接收到的信息与发送者的信息
                    String ip = receivePack.getAddress().getHostAddress();//发送者的ip
                    int port = receivePack.getPort();//发送者的端口
                    int dataLen = receivePack.getLength();//数据包长度
                    String data = new String(receivePack.getData(), 0, dataLen);//数据内容

                    System.out.println("UDPSearcher receive from ip:" + ip + "\tport:" + port + "\tdata:" + data);

                    //解析sn并添加设备
                    String sn = MessageCreator.parseSn(data);
                    if (null != sn) {
                        Device device = new Device(port, ip, sn);
                        devices.add(device);
                    }

                }
            } catch (Exception e) {

            } finally {
                close();
            }
            System.out.println("UDPSearcher listener finished");

        }

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

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


测试

首先开启UDPProvider,用于监听广播并将设备信息发送给搜索者
在这里插入图片描述
然后在开启UDPSearcher,用于发送广播并监听自己的特定端口来获取设备信息
在这里插入图片描述
可以看到,同时搜索到了2台设备。

### 回答1: UDP组播是一种在网络中传输数据的协议。它可以实现局域网内的IPC设备发现,即在局域网中通过UDP组播的方式来发现IPC设备。以下是实现这一过程的主要步骤。 首先,需要在局域网内配置一个多播组地址。多播组地址是一组预定义的IP地址,用于标识一个多播组。这个地址需要在局域网内唯一,可以通过网络管理员进行配置。 然后,IPC设备需要加入到这个多播组中。这个过程可以通过设备自身的操作或者通过网络管理员的配置来完成。设备加入多播组后,就可以接收到多播组内的消息。 接下来,需要有一个设备或者一个应用程序作为发送方。这个发送方通过UDP协议向多播组地址发送特定的发现消息。这个发现消息可以是一个特定的字符串,用来标识IPC设备。 当IPC设备收到发现消息时,会进行相应的处理。一般来说,设备会根据发现消息中的标识进行识别,如果是自己的标识,就会发送一个应答消息给发送方。 最后,发送方收到设备的应答消息后,就可以确定设备的存在,并进行相应的操作。例如,可以获取设备的IP地址、设备类型等信息,并在应用程序中显示或者进行其他操作。 总的来说,通过UDP组播可以实现局域网内的IPC设备发现。通过加入到预定义的多播组中,IPC设备可以接收到发现消息,在收到消息后发送应答消息,从而完成设备的发现。这种方式简化了设备发现的过程,提高了设备的可管理性和易用性。 ### 回答2: UDP组播是一种在局域网中实现IPC设备发现的通信方式。IPC设备是指网络摄像头、门禁系统等安防设备。组播是一种一对多的通信方式,即一个消息可以同时发送给多个接收者。 要实现局域网IPC设备发现,首先需要选择一个组播地址。组播地址是一个特殊的IP地址,用于向特定的组播组发送消息。然后,在局域网中的每个IPC设备上,都需要开启组播服务,并加入到指定的组播组中。 当一个设备想要发现其他IPC设备时,它会发送一个组播消息到组播地址。其他设备收到这个消息后,会回复一个确认消息。通过这种方式,可以实现设备之间的发现和通信。 为了确保消息的可靠传输,可以使用UDP协议来发送组播消息。UDP协议是一种简单的传输层协议,具有高效和快速的特点。通过UDP组播,可以减少通信的延迟时间和网络带宽的占用。 通过UDP组播实现局域网IPC设备发现,可以方便地管理和控制安防设备。管理员可以通过组播消息了解设备的存在和状态,并进行相应的配置调整。同时,设备之间也可以通过组播消息进行数据同步和共享。 总之,UDP组播是一种实现局域网IPC设备发现的有效通信方式。它可以帮助管理员轻松管理安防设备,并提升设备之间的协作效率。 ### 回答3: 在局域网中,为了实现IPC设备的发现和管理,可以使用UDP组播技术。UDP组播是一种将单个数据包发送给多个目标地址的通信方式。 首先,设定一个固定的组播IP地址和端口号,用于IPC设备的发现。在局域网中的每个IPC设备都会监听这个组播地址和端口号。 当一个控制中心希望发现局域网中的IPC设备,它会向组播IP地址和端口号发送一个广播消息。这个消息包含了控制中心的信息,如IP地址、端口号等。 每个IPC设备都会接收到这个广播消息并解析其中的内容。如果IPC设备符合控制中心的要求,它会发送一个响应消息给控制中心,包含了自己的信息,如IP地址、端口号、设备型号等。 控制中心接收到响应消息后,可以根据IPC设备信息进行设备的管理和控制。 UDP组播能够实现局域网中IPC设备的发现,主要有以下优势: 1. 通过单次广播消息,可以同时发现多个IPC设备,提高设备发现的效率。 2. 组播方式使用UDP协议,具有较低的网络负载和资源消耗,适用于局域网中的设备发现。 3. 消息通讯采用无连接的方式,简化了设备的配置和管理过程。 通过UDP组播实现局域网IPC设备的发现,能够方便、高效地管理和控制局域网中的设备,提升了设备管理的便利性和操作效率。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值