簡體   English   中英

在Android設備中接收UDP廣播時丟包

[英]Packet loss while receiving UDP broadcast in android device

為了從服務器接收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();
    }

UDP的問題是您的發件人(您的服務器)不知道您(您的Android設備)錯過了一些。 它不會丟失,因為你無法快速讀取它,有時只是空中干擾/擁塞或繁忙的插座。

接收者只會知道:

  1. 由於您缺少數據,因此在處理數據時會出錯
  2. 或者您的UDP數據包在其標題中按順序編號,您檢測到丟失的數字(例如,1,2,4 - 缺少3)

一旦數據包丟失,它就會丟失。 你有兩個選擇:

  1. 實現重新發送請求:一旦檢測到丟失的數據包,接收方將需要通知發送方重新發送該丟失的數據包,直到它獲得它為止,並且您的數據包處理可能會暫停,直到它發生
  2. 或者能夠忽略它,“嘿,我們可以在沒有他的情況下完成它”,並用空白數據填充(例如,位圖會有一些空白像素,如圖像損壞)
  3. 限制你的發送速度,這樣數據包就不會堵塞並丟失
  4. 數據包越小,他們就越有可能生活

(選項1:所有重發請求只是偽TCP,因此您可能只考慮放棄UDP並轉到TCP)

我認為你的問題主要是你通過wifi使用Udp Broadcast。

他們是兩個非常好的文檔答案,為什么這是一個非常慢的操作方式,以及為什么有更多的數據包丟失:

我為解決極慢的帶寬所做的事情是某種多單播協議:

  1. 管理已連接的客戶端列表。
  2. 使用發送呼叫將您服務器中的每個數據包分別發送給所有客戶端。

這是java中的代碼:

  DatagramPacket packet = new DatagramPacket(buffer, size);
  packet.setPort(PORT);

  for (byte[] clientAddress : ClientsAddressList) {
        packet.setAddress(InetAddress.getByAddress(clientAddress));
        transceiverSocket.send(packet);
  }

如果您在短脈沖串中收到多個數據報,您的接收器循環可能無法跟上,並且套接字中的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-穿孔- udpbuffer.html

[請注意,一般情況下,這將適應數據報到達的快速突發,而客戶端循環可以讀取它們 - 但如果客戶端循環長度比數據報傳輸慢,那么由於溢出,您仍會偶爾丟失。 在這種情況下,您需要加快客戶端循環,或減慢發件人的速度。

另外,正如在其他答案中另有說明的那樣,您的網絡實際上可能會丟棄數據包 - 尤其是移動網絡可能容易發生這種情況 - 因此,如果您絕對需要保證交付,則應使用TCP。 但是,如果這是您的主要問題,即使您的服務器緩慢地發送它們,您也可能會看到丟棄的數據包,而不是在突發中。

我想你通過說你只捕獲一個數據包

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服務器泵入的數據包,如果您看到多個數據包到達,那么您將確保數據包到達計算機,但是應用程序無法解決數據包

它可能有所幫助

參考上面的說明,我可以根據需要進行更改

我想您可以進行以下更改以使您的問題代碼工作而不是創建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);
    }

}

試試這應該工作!! 它可能有所幫助

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM