为啥 Java DatagramSocket 没有收到客户端发送的所有 udp 数据包?

Posted

技术标签:

【中文标题】为啥 Java DatagramSocket 没有收到客户端发送的所有 udp 数据包?【英文标题】:Why Java DatagramSocket doesn't receive all udp packet that clients have sent?为什么 Java DatagramSocket 没有收到客户端发送的所有 udp 数据包? 【发布时间】:2018-08-27 05:59:15 【问题描述】:

我的客户以高速率发送 udp 数据包。 我确定我的 java 应用程序层没有收到客户端发送的所有 udp 数据包,因为在 wireshark 中收到的数据包数量和我的 java 应用程序不匹配。 因为wireshark接收到更多的udp数据包,所以我确定udp数据包没有在网络中丢失。

代码在这里:

在一个线程中接收数据包并提供给LinkedBlockingQueue,然后在另一个线程上使用来自LinkedBlockingQueue 的数据包,然后调用onNext rx-java 主题。

socket = new DatagramSocket(this.port);
socket.setBroadcast(true);
socket.setReceiveBufferSize(2 * 1024 * 1024);

// thread-1
while (true) 
  byte[] bytes = new byte[532];
  DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
  try 
    this.socket.receive(packet);
    queue.offer(
        new UdpPacket(
            packet.getPort(), packet.getAddress().getHostAddress(), packet.getData()));
   catch (IOException e) 
    e.printStackTrace();
  



// thread-2
UdpPacket packet;
while ((packet = queue.take()) != null) 
     this.receiveMessageSubject.onNext(packet);

主机操作系统:Ubutnu 18.04

【问题讨论】:

【参考方案1】:

很难给出直接的答案,但根据我在 Java 中处理 UDP 消息的经验,提高处理消息的性能确实很重要,尤其是在处理大量数据的情况下。

所以这里有一些我会考虑的事情:

1) 您在不同队列上处理 UDP 消息是正确的。但是,队列的大小是有限的。您是否设法快速处理消息?否则,队列已满,您将阻塞 while 循环。如果是这种情况,一些简单的日志记录可以让您知道。将它们放在可以在不同步骤中排出的队列中很棒,但您还需要确保处理速度尽可能快并且队列不会填满。

2) 你所有的数据报都小于 532 字节吗?可能由于没有填满缓冲区的较大消息而发生一些丢失。

希望这会有所帮助,

【讨论】:

【参考方案2】:

我最近用另一种语言遇到了类似的问题。我不确定它在 Java 中是否同样有效,但这可能对您有所帮助。

因此,当数据包进入套接字时,它们会被缓冲,并且您已经设置了缓冲区大小,但您仍然只读取单个数据包,即使缓冲区可能容纳更多。当您一次处理一个数据报时,您的缓冲区会填得更多,最终当其满时,数据可能会丢失,因为它无法存储更多数据报。

我检查了documentation 的DatagramSocket

Receives a datagram packet from this socket

我不确定您需要在 Java 中调用哪些函数,但这里有一个我正在使用的小 sn-p。

while (!m_server->BufferEmpty()) 
        std::shared_ptr<Stream> inStream = std::make_shared<Stream>();
        std::vector<unsigned char>& buffer = inStream->GetBuffer();

        boost::asio::ip::udp::endpoint senderEndpoint = m_server->receive(boost::asio::buffer(buffer),
            boost::posix_time::milliseconds(-1), ec);

        if (ec)
        
            std::cout << "Receive error: " << ec.message() << "\n";
        
        else
        
            std::unique_ptr<IPacketIn> incomingPacket = std::make_unique<IPacketIn>();
            incomingPacket->ReadHeader(inStream);

            m_packetProcessor->ProcessPacket(incomingPacket, senderEndpoint);

            incomingPacket.reset();
        

        ++packetsRead;

        inStream.reset();


这基本上是说,如果套接字在其缓冲区中有当前帧的任何数据,则继续读取数据报,直到缓冲区为空。

不确定LinkedBlockingQueue 是如何工作的,但是如果两个线程同时尝试访问它,这也可能会导致一些问题。在您的 UDP 读取线程中,您可能会被阻塞一段时间,然后在此期间可能会收到数据包。

【讨论】:

以上是关于为啥 Java DatagramSocket 没有收到客户端发送的所有 udp 数据包?的主要内容,如果未能解决你的问题,请参考以下文章

在Java中的datagramsocket中获取源的IP地址

试着用java实现DNS——DatagramSocket, DatagramPacket, Message

Java使用DatagramSocket

DatagramSocket客户端与服务端Java实例

DatagramSocket - java.lang.NullPointerException [Android] [重复]

Java中的DatagramPacket与DatagramSocket的初步