使用 Java DatagramChannel 监听 UDP 数据报

Posted

技术标签:

【中文标题】使用 Java DatagramChannel 监听 UDP 数据报【英文标题】:Listening to UDP Datagrams using Java DatagramChannel 【发布时间】:2016-09-27 18:46:38 【问题描述】:

我正在尝试绑定 Java DatagramChannel 以侦听特定端口和子接口上的 UDP 流量,但我似乎无法让它在我创建了多个虚拟网络子接口(特别是我想要的那个)的 linux 主机上工作收听如下所示)。如果我在同一个网络 Windows PC 中托管 java 应用程序,代码就很好(使用不同的 IP 地址,但是在这种情况下,对于绑定调用,我只需要指定侦听端口,并且在不使用子接口的 Windows 上)。

这是我的 linux 机器上 ifconfig 的相关部分输出,显示了虚拟网络适配器(子接口)

p2p1:37: flags=4163<UP,BnetROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.34.1  netmask 255.255.0.0  broadcast 192.168.255.255
        ether 00:13:72:a5:9c:e2  txqueuelen 1000  (Ethernet)
        device interrupt 16

界面设置如下:

ifconfig p2p1:37 192.168.34.1 netmask 255.255.0.0

我用来初始化非阻塞 DatagramChannel 的代码如下所示:(我需要一个DatagramChannel,因为我需要使用选择器以非阻塞方式同时从多个端口读取)。我尝试使用通过channel.socket().bind(new InetSocketAddress(entry.getValue()) 存储在entry.getValue() 中的端口号仅绑定到默认适配器,但没有收到任何数据包。我在某处读到,在构造 DatagramChannel 之后,它默认构造一个绑定到“0.0.0.0”的数据报套接字,一旦绑定它就不能解除绑定——但我不确定。任何指导将不胜感激。

    @Override
    protected Task<Void> createTask() 
        return new Task<Void>() 
            @Override
            protected Void call() throws Exception 
                updateMessage("Running...");
                Map<String, DatagramChannel> dlmuChannelInfo = new HashMap<>();
                Map<String, DatagramChannel> wsuChannelInfo =  new HashMap<>();
                try 
                    // array of bytes for receiving datagrams
                    ByteBuffer rxBuffer = ByteBuffer.allocateDirect(MAX_PACKET_SIZE);

                    for (Map.Entry<String, Integer> entry : mDLMUPortInfo.entrySet()) 
                        DatagramChannel channel = DatagramChannel.open();
                        // select only works with nonblocking channels
                        channel.configureBlocking(false);

//                        NetworkInterface iface = NetworkInterface.getByInetAddress(
//                            InetAddress.getByName("192.168.34.1"));
//                        List<NetworkInterface> list = Collections.list(
//                            NetworkInterface.getNetworkInterfaces());
//                        for (NetworkInterface next : list) 
//                        

                        channel.socket().bind(new InetSocketAddress(
                            InetAddress.getByName("192.168.34.1"), entry.getValue()));
                        dlmuChannelInfo.put(entry.getKey(), channel);
                    

                    // register for reads
                    try (
                        // instantiate a selector - note that this autocloses
                        Selector selector = Selector.open()) 
                        // register a selector for reads for both the DLMU & WSU
                        Map<SelectionKey, String> keyServiceInfo = new HashMap<>();
                        for (Map.Entry<String, DatagramChannel> entry : dlmuChannelInfo.entrySet()) 
                            SelectionKey key = entry.getValue().register(
                                selector, SelectionKey.OP_READ);
                            keyServiceInfo.put(key, entry.getKey());
                        

【问题讨论】:

【参考方案1】:

您正在绑定到 192.168.34.1。您只会收到发送到该接口的数据报。除非您真的知道自己在做什么,否则您应该绑定到 0.0.0.0,这是通过省略 InetAddress 参数或使用 null 来完成的。

您关于 DatagramSocket 在构造上自己这样做的评论是不正确的。

【讨论】:

@EJB 我试过了,不幸的是它不起作用,我有一个 FTP 服务器也在这个虚拟接口上运行,我可以很好地登录,所以 IP 地址仍然有效。我遇到的唯一问题是因为它是一个子接口。我什至尝试通过子接口枚举并从特定子接口获取 InetAddress 并使用它无济于事。

以上是关于使用 Java DatagramChannel 监听 UDP 数据报的主要内容,如果未能解决你的问题,请参考以下文章

Java NIO系列教程 Java NIO DatagramChannel

Java NIO系列教程 Java NIO DatagramChannel

Java NIO学习笔记八 DatagramChannel

java-NIO-DatagramChannel(UDP)

java的nio之:java的nio系列教程之DatagramChannel

Java-NIO:DatagramChannel