Click here to Skip to main content
Click here to Skip to main content
Go to top

Issues with UdpClient.Receive

, 9 Mar 2002
Rate this:
Please Sign up or sign in to vote.
When more than one UDP packet is enqueued on a socket, UdpClient.Receive will receive packets that are not of the expected size.
<!-- Download Links --> <!-- Add the rest of your HTML here -->

Introduction

Recently I coded a prototype (for the Cebit 2002) with the release version of Visual Studio.NET. The program had to communicate with an existing server (coded in C++) via a proprietary protocol based on UDP. While doing so, I experienced some strange errors, which finally led me to a major bug in the UdpClient implementation. 

Description

When more than one UDP packet is enqueued on a socket, UdpClient.Receive will receive packets with the wrong size. The size of the packet is the sum of the sizes of all packets waiting for receive. The received packet will contain correctly the data of the first waiting packet plus so many null bytes as data is available. If you are working with binary data, you have no possibilty to determine the real size of the received packet.

Reproduction

Simply download and execute the UdpClientBug example. It will send four packets to a listening socket, and then receive those packets. You should see the following output:

UdpClient.Receive Bug
Sending 'One' (3 bytes)
Sending 'Two' (3 bytes)
Sending 'Three' (5 bytes)
Sending 'Four' (4 bytes)
Received 15 bytes, s = 'One            ', s.Length = 15
Received 12 bytes, s = 'Two         ', s.Length = 12
Received 9 bytes, s = 'Three    ', s.Length = 9
Received 4 bytes, s = 'Four', s.Length = 4

The first packet received has a size of 15 bytes, which is the sum of all send bytes (3+3+5+4 = 15). When reconverted to strings the received packets are unequal to packets which were send.

Explanation

Digging deeper, I have found an explanation what has gone wrong. The UdpClient.Receive() method returns an array of bytes. The implementation of UdpClient has to create this array with an explicit size before it can call Socket.Receive(). I guess that the implementation uses the property Socket.Available to determine the size to reserve for the array. To cite the documentation, "If you are using a message-oriented Socket type such as Dgram (UDP) the available data is the first message in the input queue.". This is wrong! Socket.Available always returns the number of bytes of all data waiting for receive. To prove this, I have written a second method DemonstrateSocketAvailableBug. The difference is, that now I am using a basic Socket to query the Socket.Available property and receive the data. It produces the following output:

Socket.Available Bug
Sending 'One' (3 bytes)
Sending 'Two' (3 bytes)
Sending 'Three' (5 bytes)
Sending 'Four' (4 bytes)
Available: 15 bytes
Received: 3 bytes, s = 'One', s.Length = 3

I guess that Socket.Available uses ioctlsocket with the FIONREAD option. A quote from the winsock documentation:

FIONREAD returns the amount of data that can be read in a single call to the recv function, which may not be the same as the total amount of data queued on the socket. If s is message oriented (for example, type SOCK_DGRAM), FIONREAD still returns the amount of pending data in the network buffer, however, the amount that can actually be read in a single call to the recv function is limited to the data size written in the send or sendto function call.

Conclusion

In conjunction with the impossibility to set a timeout while receiving data, the UdpClient class is pretty useless for receiving UDP packets. You cannot determine the size of an received packet and if a packet is lost on the network, your code will block for ever. As loss of packets is an Udp 'feature' and you have to deal with this situation. So do yourself a favor, don't use UdpClient, but use the basic socket implementation instead. But be aware, the documentation for the Socket.Available property is wrong too. 

Code Example

 
using System; 
using System.Net; 
using System.Net.Sockets;
using System.Text;

namespace UdpClientBug
{ 
  class Class1 
  {
    [STAThread]
    static void Main(string[] args) 
    {
      Bugs bugs = new Bugs(); 
      bugs.DemonstrateUdpClientBug(); 
      bugs.DemonstrateSocketAvailableBug(); 
      Console.WriteLine(); Console.WriteLine("Press enter to continue ...");
      Console.ReadLine();
    } 
  } 
  
  class Bugs 
  { 
    private IPEndPoint listenerIP = new IPEndPoint(IPAddress.Loopback, 4201); 
    public void DemonstrateUdpClientBug() 
    { 
      Console.WriteLine(); Console.WriteLine("UdpClient.Receive Bug"); 
      
      // Setup listener socket
      UdpClient listener = new UdpClient(listenerIP); // Setup sending socket
      UdpClient sender = new UdpClient(); 
      sender.Connect(listenerIP); 
      
      // Sending  three datagrams to the listener 
      Send(sender, "One"); 
      Send(sender, "Two"); 
      Send(sender, "Three"); 
      Send(sender, "Four"); 
      
      // Now receive the three datagrams from the listener
      Receive(listener);
      Receive(listener);
      Receive(listener); 
      Receive(listener); 
      
      listener.Close(); 
      sender.Close(); 
    }
    
    void Send(UdpClient sender, string s)
    {
      byte[] dgram = Encoding.ASCII.GetBytes(s);
      Console.WriteLine("Sending '" + s + "' (" + dgram.Length.ToString() 
                         + " bytes)");
      sender.Send(dgram, dgram.Length);
    }

    void Receive(UdpClient listener)
    {
      IPEndPoint from = new IPEndPoint(IPAddress.Any, 0);
      byte[] dgram = listener.Receive(ref from);
      string s = Encoding.ASCII.GetString(dgram, 0, dgram.Length);
      Console.WriteLine
      (
        "Received {0} bytes, s = '{1}', s.Length = {2}",
        dgram.Length, s, s.Length 
      );
    }

    public void DemonstrateSocketAvailableBug()
    {
      Console.WriteLine(); Console.WriteLine("Socket.Available Bug");

      Socket listener = new Socket(AddressFamily.InterNetwork, 
                                   SocketType.Dgram, ProtocolType.IP);
      listener.Bind(listenerIP);

      UdpClient sender = new UdpClient();
      sender.Connect(listenerIP);

      // Sending three datagrams to the listener
      Send(sender, "One");
      Send(sender, "Two");
      Send(sender, "Three");
      Send(sender, "Four");

      // Receiving first datagram
      Console.WriteLine("Available: {0} bytes", listener.Available);
      byte[] dgram = new byte[50];
      int nReceived = listener.Receive(dgram);
      string s = Encoding.ASCII.GetString(dgram, 0, nReceived);
      Console.WriteLine("Received: {0} bytes, s = '{1}', 
                        s.Length = {2}", nReceived, s, s.Length);
    }
  }
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Christian Rodemeyer
Architect Kabel Deutschland
Germany Germany
I'm developing for fun since 1985, starting with UCSD Pascal on some old machines (no harddisk, but four floppies!), then moving quickly on to assembler on the famous C64 and Amiga. During university I started professional development for Windows/Unix/Linux, using a myriad of languages (Pi, 386/486, Cobol, Modula2, OML, C, C++, VB, Prolog, Eiffel, Delphi, Perl, Pascal, Assembler). Currently my favorite languages are C# 3.0 and Python.

Comments and Discussions

 
QuestionFixed, now non-issue? Pinmemberpaulpv19-Feb-10 8:09 
Generalreceive udp message only Pinmemberyu-yu8-Mar-04 17:12 
QuestionSo what are you going to do? PinmemberMatt Philmon11-Mar-02 13:14 
AnswerRe: So what are you going to do? PinmemberChristian Rodemeyer12-Mar-02 10:17 
GeneralRe: So what are you going to do? PinsussAnonymous10-May-03 6:53 
GeneralRe: So what are you going to do? PinsussAnonymous9-Jun-03 23:38 
GeneralRe: So what are you going to do? Pinmemberrobdogg113-Aug-04 11:50 
GeneralThe demo works in the 1.1 framework. PinmemberRobotFood8329-Jan-05 13:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 10 Mar 2002
Article Copyright 2002 by Christian Rodemeyer
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid