首页 文章

在Android设备中接收UDP广播时丢包

提问于
浏览
7

为了从服务器接收UDP广播包到android设备,我使用服务类并在线程中侦听数据包 . 它成功接收数据包 . 问题是如果在同一时间从服务器发送多个数据包,那么将导致数据包丢失 .

我甚至尝试使用队列并在单独的线程中处理收到的数据包,然后我也没有收到数据包 . 我对网络编程完全陌生,任何帮助都会受到广泛赞赏

void startListenForUdpBroadcast() {
        UDPBroadcastThread = new Thread(new Runnable() {
            public void run() {
                try {
                    InetAddress broadcastIP = InetAddress.getByName(UdpConstants.IP_ADDRESS);
                    Integer port = UdpConstants.RECEIVER_PORT;
                    while (shouldRestartSocketListen) {
                        listenAndWaitAndThrowIntent(broadcastIP, port);
                    }

                } catch (Exception e) {
                    Log.i("UDP", "no longer listening for UDP broadcasts cause of error " + e.getMessage());
                }
            }
        });
        UDPBroadcastThread.setPriority(Thread.MAX_PRIORITY); //Setting The Listener thread to MAX PRIORITY to minimize packet loss.
        UDPBroadcastThread.start();
    }

此代码侦听新数据包并推送到队列

private void listenAndWaitAndThrowIntent(InetAddress broadcastIP, Integer port) throws Exception {
        byte[] recvBuf = new byte[64000];
        if (socket == null || socket.isClosed()) {
            socket = new DatagramSocket(port, broadcastIP);
            socket.setBroadcast(true);
        }
//socket.setSoTimeout(1000);
        DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);

        socket.receive(packet);
        messQueue.add(packet);

    }

这将检查队列中的新消息并对其进行处理

/**
     * @purpose Checking queue and If anything is added to the queue then broadcast it to UI
     */
    private void checkQueue() {
        queueThread = new Thread(new Runnable() {
            public void run() {
                try {

                    while (shouldRestartSocketListen) {
                        if (!messQueue.isEmpty()) {
                            broadcastIntent(messQueue.poll());
                        }
                    }

                } catch (Exception e) {

                }
            }
        });
        queueThread.start();
    }

5 回答

  • 2

    UDP的问题是您的发件人(您的服务器)不知道您(您的Android设备)错过了一些 . 它不会丢失,因为你无法快速读取它,有时只是空中干扰/拥塞或繁忙的插座 .

    接收者只会知道:

    • 您在处理数据时遇到错误,因为您丢失了数据

    • 或者您的UDP数据包在其 Headers 中按顺序编号,您检测到丢失的数字(例如1,2,4 - 缺少3)

    一旦数据包丢失,它就会丢失 . 你有两个选择:

    • 实现重发请求:一旦检测到丢失的数据包,接收方将需要通知发送方重新发送丢失的数据包,直到它获得它为止,并且您的数据包处理可能会暂停,直到它发生

    • 或者能够忽略它,"hey, we can do it without him",并填充空白数据(例如,位图会有一些空白像素,如图像损坏)

    • 降低发送速度,这样数据包就不会堵塞并丢失

    • 您的数据包越小,他们就越有可能生活

    (选项1:所有重发请求只是伪TCP,因此您可能只考虑放弃UDP并转到TCP)

  • 1

    我认为你的问题主要是你通过wifi使用Udp Broadcast .

    他们是两个非常好的文档答案,为什么这是一个非常慢的操作方式,以及为什么有更多的数据包丢失:

    我为解决极慢的带宽所做的事情是某种多单播协议:

    • 管理已连接的客户端列表 .

    • 使用发送呼叫将服务器中的每个数据包分别发送给所有客户端 .

    这是java中的代码:

    DatagramPacket packet = new DatagramPacket(buffer, size);
      packet.setPort(PORT);
    
      for (byte[] clientAddress : ClientsAddressList) {
            packet.setAddress(InetAddress.getByAddress(clientAddress));
            transceiverSocket.send(packet);
      }
    
  • 0

    如果在短脉冲串中收到多个数据报,则接收器循环可能无法跟上,并且套接字中的OS级RCVBUF可能会溢出(导致操作系统丢弃它确实收到的数据包) .

    如果增加RCVBUF大小,可能会更好地处理短脉冲 . 在此之前,通过socket.getReceiveBufferSize了解它已经有多大 . 还要记住,接收缓冲区中的字节数不仅要包含有效负载,还要包含在内核中存储数据包的头和sk_buff结构(参见lxr.free-electrons.com/source/include/linux) / ......) .

    您可以通过socket.setReceiveBufferSize调整接收缓冲区大小 - 但请记住,此消息只是一个提示,并且可能被内核中的设置覆盖(如果您请求的大小超过当前内核网络允许的最大大小)设置,那么你只会得到最大尺寸) .

    在请求更大的接收缓冲区之后,您应该通过调用socket.getReceiveBufferSize来仔细检查内核允许的内容 .

    如果您拥有正确的权限,则应该能够调整内核允许的最大缓冲区大小 - 请参阅例如https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Web_Platform/5/html/Administration_And_Configuration_Guide/jgroups-perf-udpbuffer.html

    [请注意,一般情况下,这将适应数据报到达的快速突发,而客户端循环可以读取它们 - 但如果客户端循环长度比数据报传输慢,那么由于溢出,您仍会偶尔丢失 . 在这种情况下,您需要加快客户端循环,或减慢发件人的速度 .

    此外,正如其他答案中另有说明的那样,您的网络实际上可能会丢弃数据包 - 尤其是移动网络可能会出现这种情况 - 因此,如果您绝对需要保证交付,则应使用TCP . 但是,如果这是您的主要问题,即使您的服务器缓慢地发送它们,您也可能会看到丢弃的数据包,而不是在突发中 .

  • 0

    我想你通过说你只捕获一个数据包

    socket.receive(packet);
    

    这是一个阻塞I / O调用,它将无限期地等待,直到它收到一个数据包,所以一旦第一个数据包到达它就是完成等待和下一个命令执行即

    messQueue.add(packet);
    

    但是,当收到多个数据包时,您需要继续接收数据包 . 在你的情况下,你刚刚在第一个包裹到达后停止接收包裹

    注意:UDP是一个不可靠的协议,不能保证数据包传输,因此可能存在数据包丢失的情况 . 但是,这对于程序的每次运行都不是问题,但是检查数据包是否是一个很好的方法是你的机器,问题在你的应用程序内(应用程序无法处理收到的数据包)使用tcpdump(它是基于Linux的操作系统或MAC的命令行实用程序)使用以下命令

    sudo tcpdump -i <interface name(one that handles traffic) eth0, eth1 .. en0, en1 (for MAC)> host <ipaddress or hostname of UDP Broadcast server>
    

    例:

    sudo tcpdump -i en1 host 187.156.13.5
    

    (如果找不到tcpdump命令,那么继续前进并安装它)

    通过使用此命令,您将看到从终端上的目标ump服务器泵入的数据包,如果您看到多个数据包到达,那么您将确保数据包到达计算机,但是应用程序无法解决数据包

    它可能有所帮助

  • 2

    参考上面的说明,我可以根据需要进行更改

    我想您可以进行以下更改以使您的问题代码工作而不是创建socket到listenAndWaitAndThrowIntent(InetAddress broadcastIP,Integer port)方法在startListenForUdpBroadcast()中创建它如下

    socket = new DatagramSocket(port, broadcastIP);
    socket.setBroadcast(true);
    while (shouldRestartSocketListen) {
         listenAndWaitAndThrowIntent(broadcastIP, port, socket);
    }
    

    现在您还需要更改listenAndWaitAndThrowIntent方法的实现,如下所示

    private void listenAndWaitAndThrowIntent(InetAddress broadcastIP, 
    Integer port, DatagramSocket socket) throws Exception {
        byte[] recvBuf = new byte[64000];
        //socket.setSoTimeout(1000);
        //change value of condition in for loop to desired number of packets you would like to receive in below example it is 2 so it captures two packets
        for (int i=0 ; i <= 2 ; i++ ){
        DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
    
        socket.receive(packet);
        messQueue.add(packet);
        }
    
    }
    

    试试这应该工作!!它可能有所帮助

相关问题