Re: Possible bug in .Net 2.0 udp sockets?



Pardon me for butting in, but you should be able to increase the size of the
queue of UDP messages. We do this (in C++) with the following:

int receivesize = RECEIVEQUEUESIZE;
if (0 != setsockopt(mysocket,SOL_SOCKET,SO_RCVBUF,
(char *)&receivesize,sizeof(receivesize)))


where RECEIVEQUEUESIZE is a #define of 1024*1024

You won't miss any UDP packets with a buffer that large!

al


"Vadym Stetsyak" <vadym_s@xxxxxxx> wrote in message
news:eT6Ncnv5GHA.1256@xxxxxxxxxxxxxxxxxxxxxxx
Hello, Redge!

R> I called BeginReceiveFrom() several times on purpose, since I do not
R> miss any data that arrives at the socket. Have a look at the
R> following
R> code, which is my callback method:

R> public void OnReceive(IAsyncResult result)
R> {
R> SocketState currSocketState = result.AsyncState as SocketState;

R> try
R> {
R> EndPoint remoteEndPoint = new IPEndPoint(0, 0);
R> int bytesRead =
R> currSocketState.UdpSocket.EndReceiveFrom(result,
R> ref remoteEndPoint);

R> // ... do some stuff
R> }
R> finally
R> {
R> currSocketState.BeginReceive(new
R> AsyncCallback(this.OnReceive));
R> }
R> }

That is correct, implementation. And your app won't miss any data, because
after receiving
data you issue another BeginReceiveFrom call - this is correct.

If you don't do that, indeed, UDP stack can drop packets.
But this "dropping" occurs under certain circumstances.

I'll try to explain why this can happen. When Udp stack receives a packet,
it stores it in the queue. It does so with every pending packet. This
queue has
a limit, connected with the size of occupied memory. When this limit is
reached
all subsequent UDP packets will be dropped by the stack.

When you call ReceiveFrom or BeginReceiveFrom you're taking
that packet out of queue ( data is copied to the buffer supplied by the
caller ).

R> If I call BeginReceiveFrom() only once, my server will have a "blind
R>
R> spot" in the time between entering the callback and starting
R>
R> BeginReceiveFrom() again!


R> The msdn article I referred to in my last
R> mail puts it the following
R> way:

R> [quote]
R> UDP will (...) drop packets
R> upon receipt if, even momentarily,
R> BeginReceiveFrom is not active on
R> the socket.

R> (This) problem is the one most easily solved. In my sample
R> receiver
R> code, there's a short span of time between acceptance of a
R> packet and
R> calling another BeginReceiveFrom. Even if this call were the
R> first
R> one
R> in MessageReceivedCallback, there's a still short period
R> when the app
R> isn't listening. One improvement would be activating
R> several
R> instances
R> of BeginReceiveFrom, each with a separate buffer to
R> hold received
R> packets.
R> [/quote]


See the above.

Doing several BeginReceiveFrom calls can lead to unpredictable behavior


To minimize the time between EndReceiveFrom and subsequent BeginReceivFrom
you can enqueue data for processing. The algorithm can be the following:
- received data
- enqueud them in the queue
- issued another receive data

While another thread is taking data from the queue for further processing.

R> And that's exactly what I did. So,
R> you are right, when I limit the
R> number of simultaneous listeners to 1,
R> my application works. But I
R> will
R> lose a lot of incoming packets,
R> especially if there is a high
R> workload.

Did you test packet loss percentage?
Add a counter to the message and check that counter
every time you receive a message.

R> So there is still the
R> question: Is something wrong in my
R> implementation
R> of calling
R> BeginReceiveFrom() multiple times, or is there an error in
R> the .Net
R> Framework? I suppose it's my error, but what is strange is
R> that
R> it
R> works in .Net 1.1 without any problems ...

IMO it is not stable solution.

While experimenting with
your code, I've added two calls to Console.WriteLine().
One after Send and one after Receive, and problem dissapeared.

So, this means that the fact that the code worked under .NET 1.1
doesn't mean that the code is correct.

R> Greetings,
R> Daniel

R> Vadym
R> Stetsyak wrote:
Hello, Redge!

There is subtle bug in your
R> implementation.

You have multiple listeners and only one socket and
R> one endpoint.
There is no
purpose creating mutiple listeners. If you
R> had multiple endpoints then
it is necessary
to create one listener
R> per endpoint.

I changed your code and bug vanished.

public
R> Receiver(int numListeners)
{
this.inPort = 10000;

R> this.numListeners = 1;//= numListeners;
....
}


R> A while ago,
R> I created a client-server UDP application in .Net 1.1.
A
R> client
R> application connects via UDP to a server; once acknowledged,
R> the

R> R> server sends the requested data to the client (a video stream).

R>
R> The application has to work over the internet (where the data may
R>
R> cross
R> NATs, routers and firewalls on its way). That's why the
R> client
R> initiates
R> the data transfer, and the server examines the
R> EndPoint-object to
R> find
R> out where the request originates, and
R> where to send the data.

R> As recommended by an msdn-article
R>
R> (http://msdn.microsoft.com/msdnmag/issues/06/02/UDP/),
R> I use one
R> socket to listen on a port, and run the command
R> "BeginReceiveFrom()"
R> multiple times, each time with a separate
R> buffer.
R> In .Net 1.1,
R> this works just fine: in the callback, I call
R> "EndReceiveFrom()" to
R> get the data and the endpoint, and use this
R> endpoint to send the
R> requested data to.

R> However, in .Net 2.0, I get a wrong endpoint from
R> time to time!
R> I tested this with a simple application:

R> -
R> Several sockets that send data, each sending from (= binded to) a
R>
R> different port
R> - The data that is sent contains the port it was
R> sended from
R> - One socket listening for incoming udp-packets,
R> constructed as
R> described above (several calls to
R> "BeginReceiveFrom()" with
separate
R> buffers).

R> The test
R> application can run on a single computer or on several
R> computers
R> within the same network. In each case, the application
R> compares the
R> port saved in the EndPoint-object with the port
R> specified
R> in
R> the received data.

R> - In .Net 1.1, this works just fine: the port of
R> the
EndPoint-object
R> and
R> in the received buffer are always
R> equal, BUT:
R> - In .Net 2.0, I get lots of wrong ports in the
R> beginning --
R> normally,
R> the port is always false when
R> "EndReceiveFrom()" is called THE
FIRST
R> TIME for a buffer; the
R> subsequent calls seem to work fine.
(However,
R> in
R> my REAL
R> application, wrong port-information is not only found in
the
R>
R> beginning, but also later on during runtime).

R> It is obvious that
R> this error can lead to real problems in my
R> application, since I send
R> data to the wrong clients. It is of
course
R> possible that I just
R> made a mistake in my implementation -- a
mistake
R> that happens to
R> have no effect in .Net 1.1, while .Net 2.0 is more
R> delicate in that
R> respect.

R> However, I did not find anything drastically different from
R> the
msdn
R> samples. I made a class "SocketState", and one instance
R> of this
class
R> is
R> used for ever "BeginReceiveFrom()":

R>
R> class SocketState
R> {
R> private Socket udpSocket;
R>
R> private byte[] buffer;
R> private int bufferSize;
R> private
R> EndPoint remoteEndPoint;

R> public SocketState(Socket socket, int
R> bufferSize)
R> {
R> this.udpSocket = socket;
R>
R> this.bufferSize = bufferSize;
R> this.buffer = new
R> byte[bufferSize];
R> this.remoteEndPoint = (EndPoint)
R>
R> (new IPEndPoint(IPAddress.Any, 0));
R> }

R> public void
R> BeginReceive(AsyncCallback callback)
R> {
R> this.buffer
R> = new byte[bufferSize];
R> this.remoteEndPoint = (EndPoint)

R> R> (new IPEndPoint(IPAddress.Any, 0));

R>
R> this.udpSocket.BeginReceiveFrom(this.buffer, 0,
R>
R> this.buffer.Length,
R> SocketFlags.None, ref
R> this.remoteEndPoint,
R> callback, this);
R> }

R>
R> // properties omitted
R> }

R> So I create one listening socket and a
R> number of SocketStates,
which
R> all
R> get a reference to this
R> socket. Then I call "BeginReceive()" on
every
R> SocketState
R> instance; in the specified callback, I compare buffer
and
R>
R> endpoint.

R> In case somebody wants to take a closer look, the source
R> code can
be
R> downloaded here:

R>
R> http://www.incognitek.com/user/stuff/MassiveSocketTest.zip

R> The zip
R> file contains projects for Visual Studio 2003 and 2005.

R> Any help
R> would be appreciated!
R> Greetings,

R> Daniel Sperl, Funworld AG

R>
R> Mail: daniel[DOT]sperl[AT]funworld[DOT]com

--
Regards, Vadym
R> Stetsyak
www: http://vadmyst.blogspot.com
d be appreciated!
R> Greetings,

R> Daniel Sperl, Funworld AG

R> Mail: daniel[DOT]sperl[AT]funworld[DOT]com

--
Regards, Vadym Stetsyak
www: http://vadmyst.blogspot.com

--
Regards, Vadym Stetsyak
www: http://vadmyst.blogspot.com


.