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

时间:2016-08-11 08:51:47

标签: android network-programming udp datagram packets

为了从服务器接收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 个答案:

答案 0 :(得分:2)

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

接收者只会知道:

  1. 由于您缺少数据而在处理数据时出现错误
  2. 或者您的UDP数据包在其标题中按顺序编号,您检测到丢失的数字(例如1,2,4 - 缺少3)
  3. 一旦数据包丢失,它就会丢失。你有两个选择:

    1. 实现重新发送请求:一旦检测到丢失的数据包,接收方将需要通知发送方重新发送该丢失的数据包,直到它获得它为止,并且您的数据包处理可能会暂停,直到它出现
    2. 或者能够忽略它,“嘿,我们可以在没有他的情况下完成”,并填入空白数据(例如,位图会有一些空白像素,如图像损坏)
    3. 降低发送速度,使数据包不会堵塞并丢失
    4. 您的数据包越小,他们就越有可能生活
    5. (选项1:所有重发请求只是伪TCP,因此您可能只考虑放弃UDP并转到TCP)

答案 1 :(得分:2)

我认为您的问题主要是您通过wifi使用Udp广播。

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

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

  1. 管理您已连接的客户列表。
  2. 通过发送电话将您服务器中的每个数据包分别发送给所有客户。
  3. 这是java中的代码:

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

答案 2 :(得分:1)

如果在短脉冲串中收到多个数据报,则接收器循环可能无法跟上,并且套接字中的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。但是,如果这是您的主要问题,即使您的服务器缓慢地发送它们,也不会突然发送,您可能会看到丢弃的数据包。]

答案 3 :(得分: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服务器泵入的数据包,如果您看到多个数据包到达,那么您将确保数据包到达计算机,但应用程序不足以解决数据包

它可能会有所帮助

答案 4 :(得分:0)

参考上面的说明,我可以根据需要进行更改,以使代码按照要求行事

我想您可以进行以下更改以使您的问题代码工作而不是创建套接字到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);
    }

}

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