Android中UDP多播的数据包丢失

时间:2016-03-17 21:25:48

标签: android udp multicast multicastsocket

我创建了一个应用程序,它提供了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%的数据包都会丢失。这意味着,如果我在接收端以5ms的间隔发送1000个512字节的数据包50-70个数据包丢失。

有没有办法可以减少丢包?或者我的代码是否有可能改进,以便减少数据包丢失?

提前致谢。

2 个答案:

答案 0 :(得分:1)

你无能为力。

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

  2. wifi上的数据包丢失将根据接入点及其运行的设备的不同品牌/型号/配置而有所不同。

    例如,5ghz可能表现更好,因为空气波通常更清洁,但它不会穿透墙壁以及2.4 ghz。

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

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

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

    在多播中“处理”数据包丢失的方法:

    前向纠错。

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

    失去隐瞒

    • 您可以尝试估算损失之间的数据。

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

答案 1 :(得分:0)

您是否尝试将间隔增加到> 5毫秒?您是否也试过测试此应用程序哪里没有对wifi的干扰?