Android 中的 UDP 多播丢包

Packet losses in UDP multicast in Android

我创建了一个应用程序,它提供了 android 中 UDP 数据包丢失的统计信息。这是我的应用程序架构。 1)多播UDP数据包的应用程序。下面是它的代码:

package rockwell.multicastserverproj;

import android.content.Context;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class MainActivity extends AppCompatActivity {

    EditText txtMsg;
    EditText txtPackets;
    EditText txtMs;
    EditText txtBytes;
    EditText txtCount;
    byte[] rtpData;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();

            }
        });

        /*Thread thrClient = new Thread(new ReceiveMulticast());
        thrClient.start();*/

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    public void onBtnClicked(View view)
    {
        Thread threadTotalPackets = new Thread(new SendMulticast());
        threadTotalPackets.start();

        Thread thread = new Thread(new MulticastPackets());
        thread.start();
        //Toast.makeText(this,"Message multicasted",Toast.LENGTH_LONG).show();


    }

    public class MulticastPackets implements Runnable{

        @Override
        public void run() {
            InetAddress group = null;
            MulticastSocket multiSocket = null;
            int PORT = 6500;
            txtPackets =(EditText)findViewById(R.id.txtPackets);
            txtMs = (EditText)findViewById(R.id.txtMs);
            txtBytes = (EditText)findViewById(R.id.txtBytes);
            txtCount = (EditText)findViewById(R.id.txtCount);

            int noOfPackets = Integer.parseInt(txtPackets.getText().toString());
            int delayMS = Integer.parseInt(txtMs.getText().toString());
            int packetSize = Integer.parseInt(txtBytes.getText().toString());
            int cntPacket = Integer.parseInt(txtCount.getText().toString());

            WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
            WifiManager.MulticastLock mLock = wifi.createMulticastLock("mylock");
            mLock.acquire();

            try{
                group = InetAddress.getByName("230.0.0.1");
                multiSocket = new MulticastSocket(PORT);
            } catch (IOException e) {
                e.printStackTrace();
            }

            for(int pcktCnt=1; pcktCnt<=noOfPackets; pcktCnt++) {

                rtpData = new byte[packetSize];
                int cnt = unsigned_int(pcktCnt);
                byte[] seqArr = null;
                seqArr = toBytes(cnt);
                byte varFirst = 0xa;
                byte varSecond = 0x5;

                for(int i=4;i<packetSize;i+=2)
                {
                    if(i%4 ==0) {
                        rtpData[i] = varFirst;
                        rtpData[i + 1] = varFirst;
                    }
                    else {
                        rtpData[i] = varSecond;
                        rtpData[i + 1] = varSecond;
                    }
                }

                for(int i=0;i<4;i++)
                {
                    rtpData[i] = seqArr[i];
                }

                DatagramPacket requestPacket = new DatagramPacket(rtpData, rtpData.length, group, PORT);
                try {
                    for(int i=0;i<cntPacket;i++) {
                        multiSocket.send(requestPacket);
                        Thread.sleep(delayMS, 0);
                    }
                    int test = fromByteArray(seqArr);
                    Log.i("Multicast", "Packet send. Sequence number is: " + test);

                } catch (Exception e) {
                    e.printStackTrace();
                }
                int test = fromByteArray(seqArr);
            }

            try{

                multiSocket.leaveGroup(group);
                multiSocket.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
            mLock.release();

            MainActivity.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this,"Packet sent",Toast.LENGTH_LONG).show();
                }
            });
        }
    }

    public class SendMulticast implements Runnable{
        @Override
        public void run() {
            InetAddress group = null;
            MulticastSocket multiSocket = null;
            int PORT = 5500;
            txtPackets =(EditText)findViewById(R.id.txtPackets);
            txtBytes = (EditText)findViewById(R.id.txtBytes);

            String requestString = txtPackets.getText().toString();
            String strPackSize = txtBytes.getText().toString();

            requestString = requestString +";" + strPackSize;
            Log.i("reqstring",requestString);
            byte[] requestData = new byte[requestString.length()];
            requestData = requestString.getBytes();

            WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
            WifiManager.MulticastLock mLock = wifi.createMulticastLock("mylock");
            mLock.acquire();

            try{
                group = InetAddress.getByName("230.0.0.1");
                multiSocket = new MulticastSocket(PORT);
                multiSocket.joinGroup(group);
            } catch (IOException e) {
                e.printStackTrace();
            }

            try{
                DatagramPacket requestPacket = new DatagramPacket(requestData, requestData.length, group, PORT);
                multiSocket.send(requestPacket);
                Log.i("multicastproj","message multicasted");

            } catch (IOException e) {
                e.printStackTrace();
            }
            try{
                multiSocket.leaveGroup(group);
                multiSocket.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
            mLock.release();
        }
    }

    static int unsigned_int(int nb) {
        if (nb >= 0)
            return (nb);
        else
            return (256 + nb);
    }

    public byte[] toBytes(int i)
    {
        byte[] result = new byte[4];

        result[0] = (byte) (i >> 24);
        result[1] = (byte) (i >> 16);
        result[2] = (byte) (i >> 8);
        result[3] = (byte) (i /*>> 0*/);

        return result;
    }

    public int fromByteArray(byte[] bytes) {
        return bytes[0] << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 0xFF) << 8 | (bytes[3] & 0xFF);
    }



    public class ReceiveMulticast implements Runnable{
        @Override
        public void run() {
            byte[] requestData = new byte[1024];
            InetAddress group = null;
            MulticastSocket multiSocket = null;
            int PORT = 4500;

            WifiManager wifi = (WifiManager) getSystemService(getApplicationContext().WIFI_SERVICE);
            WifiManager.MulticastLock mLock = wifi.createMulticastLock("mylock");
            mLock.acquire();

            try{
                group = InetAddress.getByName("230.0.0.1");
                multiSocket = new MulticastSocket(PORT);
                multiSocket.joinGroup(group);
            } catch (IOException e) {
                e.printStackTrace();
            }

            try{
                while(true)
                {
                    DatagramPacket requestPacket = new DatagramPacket(requestData, requestData.length);
                    multiSocket.receive(requestPacket);

                    String requestString = new String(requestPacket.getData(), 0, requestPacket.getLength());
                    Log.d("CreateMulticastServer", "Got request = " + requestString);

                    /*txtMsg = (EditText)findViewById(R.id.txtMsg);
                    txtMsg.setText(requestString);*/
                }

            } catch (IOException e) {
                e.printStackTrace();
            }

            try{
                multiSocket.leaveGroup(group);
                multiSocket.close();

            } catch (IOException e) {
                e.printStackTrace();
            }

            mLock.release();

        }
    }
}

接收这些组播 UDP 数据包的另一个应用程序

持续运行并接收组播数据包的服务:

package rockwell.packetstatistics;

import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

/**
 * Created by mmjoshi on 2/10/2016.
 */
public class PacketReceive_Service extends IntentService {
    boolean flag = true;
    int packetSize = 0;
    public PacketReceive_Service() {
        super("PacketReceive_Service");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        String strVar = intent.getStringExtra("vari");
        Log.i("onstartservice","string is " + strVar);

        Thread thread = new Thread(new PacketThread());
        thread.setPriority(Thread.MAX_PRIORITY);
        thread.start();

        Thread thread1 = new Thread(new TotalPackets());
        thread1.start();
    }

    public class PacketThread implements Runnable
    {
        @Override
        public void run() {
            InetAddress group = null;
            MulticastSocket multiSocket = null;
            WifiManager.MulticastLock mLock = null;
            int prevSeqNo=0;
            String strMissingPackets="";
            int TotalpacketsReceived = 0;
            try {

                if(packetSize == 0)
                    packetSize = 1036;

                byte[] requestData = new byte[packetSize];
                int PORT = 6500;
                byte varFirst = 0xa;
                byte varSecond = 0x5;

                WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
                mLock = wifi.createMulticastLock("mylock");
                mLock.setReferenceCounted(true);
                mLock.acquire();

                try{
                    group = InetAddress.getByName("230.0.0.1");
                    multiSocket = new MulticastSocket(PORT);
                    multiSocket.joinGroup(group);
                } catch (IOException e) {
                    e.printStackTrace();
                }

                while (flag) {
                    final DatagramPacket requestPacket = new DatagramPacket(requestData, requestData.length);
                    multiSocket.receive(requestPacket);

                    byte[] resultData = requestPacket.getData();

                    byte[] seqArr = new byte[4];
                    for(int i=0;i<4;i++){
                        seqArr[i] = resultData[i];
                    }

                    int seqNo = fromByteArray(seqArr);
                    Log.i("RecvPackets","multiple packet received # is: " + seqNo);
                    if(prevSeqNo!=seqNo)
                    {
                        TotalpacketsReceived++;
                        if(prevSeqNo!=0)
                        {
                            if((seqNo - prevSeqNo)>1)
                            {
                                for(int k=(prevSeqNo+1);k<seqNo;k++)
                                {
                                    strMissingPackets += k + ", ";
                                    sendResultMessage("Missing;" + String.valueOf(k));
                                    Log.i("RecvPackets","Packet missing. Missing# is: " + k);
                                }
                            }
                        }
                        for(int i=4;i<packetSize;i+=2)
                        {
                            if(i%4 ==0) {
                                if(resultData[i] != varFirst ||  resultData[i+1] != varFirst)
                                {
                                    if(seqNo != 1) {
                                        sendResultMessage("DataError;" + String.valueOf(seqNo));
                                        Log.i("DataCheck", "Error in data");
                                    }
                                }
                            }
                            else {
                                if(resultData[i] != varSecond ||  resultData[i+1] != varSecond)
                                {
                                    if(seqNo != 1) {
                                        sendResultMessage("DataError;" + String.valueOf(seqNo));
                                        Log.i("DataCheck", "Error in data");
                                    }
                                }
                            }
                        }
                        prevSeqNo = seqNo;
                        Log.i("MulticastService", "Packet size is: " + packetSize + " Packet receive. Sequence number is: " + seqNo);
                        sendResultMessage("TotalPacketsReceived;" + String.valueOf(TotalpacketsReceived));
                    }
                }


            } catch (IOException e) {
                Log.i("DEU Service", "In cache");
                flag = false;
                e.printStackTrace();
            }
            finally {
                try {
                    if(multiSocket != null) {
                        if(group != null)
                            multiSocket.leaveGroup(group);
                        multiSocket.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }

                if(mLock != null)
                    mLock.release();
            }
        }
    }

    private void sendResultMessage(String strPacks) {
        Intent intent = new Intent("intData");
        intent.putExtra("result",strPacks);
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }
    private void sendTotalPackets(String strPacks) {
        Intent intent = new Intent("intPacket");
        intent.putExtra("result",strPacks);
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }
    public int fromByteArray(byte[] bytes) {
        return bytes[0] << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 0xFF) << 8 | (bytes[3] & 0xFF);
    }

    public class TotalPackets implements Runnable{
        @Override
        public void run() {
            byte[] requestData = new byte[1024];
            InetAddress group = null;
            MulticastSocket multiSocket = null;
            int PORT = 5500;

            WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
            WifiManager.MulticastLock mLock = wifi.createMulticastLock("mylock");
            mLock.acquire();

            try{
                group = InetAddress.getByName("230.0.0.1");
                multiSocket = new MulticastSocket(PORT);
                multiSocket.joinGroup(group);
            } catch (IOException e) {
                e.printStackTrace();
            }

            try{
                while(true)
                {
                    DatagramPacket requestPacket = new DatagramPacket(requestData, requestData.length);
                    multiSocket.receive(requestPacket);

                    String requestString = new String(requestPacket.getData(), 0, requestPacket.getLength());
                    Log.i("requestString",requestString);
                    String[] spltStr = requestString.split(";");
                    packetSize = Integer.parseInt(spltStr[1].toString());
                    Log.i("service","Packet size is: " + spltStr[1].toString() + " Total Packs: " + spltStr[0]);
                    sendTotalPackets(spltStr[0].toString());
                    /*txtMsg = (EditText)findViewById(R.id.txtMsg);
                    txtMsg.setText(requestString);*/
                }

            } catch (IOException e) {
                e.printStackTrace();
            }

            try{
                multiSocket.leaveGroup(group);
                multiSocket.close();

            } catch (IOException e) {
                e.printStackTrace();
            }

            mLock.release();
        }
    }
}

所以,我的问题是,在服务端,当我收到数据包时,大部分数据包丢失了 5-7%。这意味着,如果我以 5 毫秒的间隔发送 1000 个 512 字节的数据包,接收端将丢失 50-70 个数据包。

有什么办法可以减少丢包吗?或者我的代码是否有任何改进的机会,以便减少丢包?

提前致谢。

您是否尝试将间隔增加到 >5 毫秒?您是否也尝试在没有 wifi 干扰的情况下测试此应用程序?

你无能为力。

  1. UDP 是一个没有传递保证的传输层。

  2. wifi 上的数据包丢失会因接入点和运行设备的不同 makes/models/configurations 而异。

    例如,5ghz 可能表现更好,因为空气波通常更干净,但它不像 2.4 ghz 那样穿透墙壁。

并非所有 Android 设备都使用相同的 wifi 无线电。

如果您使用在 MAC 层进行多播到单播转换的接入点,您实际上可以获得几乎 0% 的数据包丢失。所以你在你的应用软件中使用UDP。然后接入点在 802.11 层内部进行重传。例如,Cisco 和 Xirrus 具有多播到单播的转换选项,并且产生几乎 0% 的数据包丢失。然而,这是要付出代价的,因为它不能很好地扩展,因为每个多播流都是单独发送到每个订阅的设备。

嗯...你能用软件做什么....

“处理”多播丢包的方法:

前向纠错。

  • 您可以将每个数据包发送两次。 AABBCCDDEE
  • 您可以发送每个数据包两次但延迟:ABACBDCED
  • 部分数据可以发送两次:AABCCDE 任何前向纠错方案都会增加带宽并增加延迟。

丢失隐瞒

  • 你可以尝试估计损失之间的数据。

您没有真正陈述您的申请,因此很难就损失处理提出建议。由于你的 5ms 限制,听起来你正在传输 48khz 音频的 240 帧数据包。