2

I have created an application which gives statistics for the UDP packet loss in android. This is my architecture of the applications. 1) An application which multicast the UDP packets. Below is the code for it:

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();

        }
    }
}

Another application which receives those multicast UDP packets

Service which runs continuously and receives the multicast packet:

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();
        }
    }
}

So, my question is, at the service side when I receives the packet mostly 5-7% of packets are loss. It means, if I send 1000 packets of 512 bytes at 5ms of interval 50-70 packet losses at the receiving end.

Is there a way I can reduce this packet loss? Or Is there any chances of my code for improvement so that packet loss can be reduced?

Thanks in advance.

MMJ
  • 519
  • 1
  • 9
  • 26

2 Answers2

1

There's not much you can do.

  1. UDP is a transport layer that does not have delivery guarantees.

  2. Packet loss over wifi will vary based on the different makes/models/configurations of the access points and the device it runs on.

    For example, 5ghz may perform better as the air waves are usually cleaner but it does not penetrate through walls as well as 2.4 ghz.

Not all Android devices use the same wifi radios.

You can actually get practically 0% packet loss if you use an access point that does multicast to unicast conversion at the MAC layer. So you use UDP in your application software. Then the access point internally does retransmissions at the 802.11 layer. For example, Cisco, and Xirrus have multicast to unicast conversion options and yield practically 0% packet loss. However, there is a price to pay as it does not scale very well because each multicast stream is sent individually to each subscribed device.

Well... What can you do in software....

Ways to "deal" with packet loss in multicast:

Forward error correction.

  • You can send each packet twice. AABBCCDDEE
  • You can send each packet twice but delayed: ABACBDCED
  • You can send part of the data twice: AABCCDE Any forward error correction scheme ads more bandwidth and also increases latency.

Loss concealment

  • You can try to estimate the data between losses.

You haven't really stated your application so it is difficult to make suggestions on dealing with losses. With your 5ms limitation, it sounds like you are transmitting 240 frame packets of 48khz audio.

Community
  • 1
  • 1
jaybers
  • 1,991
  • 13
  • 18
  • Right now, i am just analyzing the packet loss and making the packet statistics matrix. Yes, definitely I'll use this UDP packets for the audio streaming. – MMJ Mar 23 '16 at 20:51
0

Did you try to increase the interval to >5ms? Also did you try to test this application where there was no kind of interference for wifi?

pooja
  • 1
  • 4
  • There's a limitation for me to increase the interval > 5ms. I am going to test it in a single wifi zone only. But for now I have not tested it in such an environment. – MMJ Mar 17 '16 at 22:21
  • @MMU You have to make up your mind. The only way to reduce the packet loss is to reduce the network load. Either increase the interval or accept the current loss rate. – user207421 Mar 18 '16 at 00:40
  • @EJP, thanks for your inputs. I'll think on it to go ahead as it is, or else if I found anything I'll post here – MMJ Mar 18 '16 at 16:18
  • Try reducing the size of the packet <512bytes? – pooja Mar 21 '16 at 17:48
  • as per the requirement it should be atleast 512 bytes – MMJ Mar 23 '16 at 20:49