views:

145

answers:

0

Hi,

I am writing a UDP receiver using UdpClient class.

IPEndPoint broadcastAddress = new IPEndPoint(IPAddress.Any, Settings.Default.SenderPort);
UdpClient udpClient = new UdpClient();
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 102400);
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
udpClient.ExclusiveAddressUse = false;
udpClient.Client.Bind(broadcastAddress);

Note 100kB socket receive buffer.

Then I receive packets in a loop. On popular demand I am listing the whole loop code :-) Keep in mind this is only a test application. Code might be optimized etc. but it works fine and is fast enough.

// receive
while (true)
{
    // Blocks until a message returns on this socket from a remote host.
    Byte[] receiveBytes = udpClient.Receive(ref broadcastAddress);

    int deviceID = BitConverter.ToInt32(receiveBytes, 0);
    int packetID = BitConverter.ToInt32(receiveBytes, 4);

    if (!stw.IsRunning)
    {
        stw.Start();
        nextExpectedPackets[deviceID] = packetID + 1;
        continue;
    }

    // do we have this device on list?
    if (!nextExpectedPackets.TryGetValue(deviceID, out nextExpectedPacketID))
    {
        // add device id
        nextExpectedPackets.Add(deviceID, packetID);
    }

    // have we missed a packet?
    if (nextExpectedPacketID != packetID)
    {
        if (nextExpectedPacketID < packetID)
            packetsLost += packetID - nextExpectedPacketID;
        else
            packetsLost = 0;
    }
    else
        packetCount++;

    // next expected packet id
    nextExpectedPackets[deviceID] = packetID + 1;

    if (packetCount>0 && packetCount % 100000 == 0)
        Console.WriteLine(string.Format("Received {0} packets in {1:f} sec ({2:d}/sec) and lost {3} ({4:f6}%)", packetCount, stw.Elapsed.TotalSeconds, packetCount / (long)stw.Elapsed.TotalSeconds, packetsLost, (double)packetsLost / packetID * 100));
}

As UDPs are volatile it is important to receive them as quickly as possible. To help this I make the process run at ProcessPriorityClass.RealTime. This works fine if receiver does nothing but receiving packets.

For tests I run a background thread with ThreadPriority.Lowest that looks like this:

static private void LoadSimulator2(object arg)
{
    byte[] numbers = new byte[40000];
    Random rnd = new Random();

    while (true)
    {
        // fill array
        rnd.NextBytes(numbers);

        // sort
        Array.Sort(numbers);

        Thread.Sleep(0);
    }
}

This has some impact on packets drop rate but is acceptable. Problem arises when that thread is not yielding i.e. when I comment Thread.Sleep(0). Drop rate becomes significant and unacceptable.

Now, I know the obvious answer: make sure background processing is low or yields often. Thing is that I cannot know what will be happening in the background. So what I am looking for is a way to make sure background thread does not get in a way of the main thread. I am sure it can be done in general - look at Skype for example. Any suggestions welcome.

Thanks, M.