Asynchronous UDP socket problems
I'm struggling a bit with socket programming (something I'm not familiar with at all) and I can't find anything that helps from Google or MSDN (terrible). Sorry for the length of this.
Basically I have an existing service that receives and responds to requests over UDP. I can't change that at all.
I also have a client in my webapp that sends and listens for responses to this service. The existing client I was provided with is a singleton that creates a socket and an array of response slots and then creates a background thread with an infinite looping method that makes "sock.Receive ()" calls and pushes the data received into the slot. All sorts of things about this seem to be wrong to me and an endless stream is interrupting my unit testing, so I am trying to replace this service with one that makes it send / receive asynchronously.
Item 1: Is this correct? I want a non-blocking, scalable, thread-safe service.
My first try was roughly what worked, but the data I got was always shorter than expected (i.e. the buffer didn't have the number of bytes requested) and seemed to throw exceptions on processing.
private Socket MyPreConfiguredSocket;
public object Query()
{
//build a request
this.MyPreConfiguredSocket.SendTo(MYREQUEST, packet.Length, SocketFlags.Multicast, this._target);
IAsyncResult h = this._sock.BeginReceiveFrom(response, 0, BUFFER_SIZE, SocketFlags.None, ref this._target, new AsyncCallback(ARecieve), this._sock);
if (!h.AsyncWaitHandle.WaitOne(TIMEOUT)) { throw new Exception("Timed out"); }
//process response data (always shortened)
}
private void ARecieve (IAsyncResult result)
{
int bytesreceived = (result as Socket).EndReceiveFrom(result, ref this._target);
}
My second attempt was based on more google trawl and this recursive pattern I've seen a lot, but this version always doesn't work! It never gets to ARecieve.
public object Query()
{
//build a request
this.MyPreConfiguredSocket.SendTo(MYREQUEST, packet.Length, SocketFlags.Multicast, this._target);
State s = new State(this.MyPreConfiguredSocket);
this.MyPreConfiguredSocket.BeginReceiveFrom(s.Buffer, 0, BUFFER_SIZE, SocketFlags.None, ref this._target, new AsyncCallback(ARecieve), s);
if (!s.Flag.WaitOne(10000)) { throw new Exception("Timed out"); } //always thrown
//process response data
}
private void ARecieve (IAsyncResult result)
{
//never gets here!
State s = (result as State);
int bytesreceived = s.Sock.EndReceiveFrom(result, ref this._target);
if (bytesreceived > 0)
{
s.Received += bytesreceived;
this._sock.BeginReceiveFrom(s.Buffer, s.Received, BUFFER_SIZE, SocketFlags.None, ref this._target, new AsyncCallback(ARecieve), s);
}
else
{
s.Flag.Set();
}
}
private class State
{
public State(Socket sock)
{
this._sock = sock;
this._buffer = new byte[BUFFER_SIZE];
this._buffer.Initialize();
}
public Socket Sock;
public byte[] Buffer;
public ManualResetEvent Flag = new ManualResetEvent(false);
public int Received = 0;
}
Point 2: It's so clear that something is wrong with me.
Point 3: I'm not sure if I'm right. How does the data coming from the remote service even get into the right listening thread? Do I need to create a socket for every request?
Out of my comfort zone here. Need help.
Not a solution for you, just a suggestion - come up with the simplest code that works by shooting all threads / events / etc. From there, start adding the necessary and only necessary complexity. My experience has always been that in the process, I would discover what I was doing wrong.
a source to share
So your SUDO diagram of your program looks like this:
Socket MySocket;
Socket ResponceSocket;
byte[] Request;
byte[] Responce;
public byte[] GetUDPResponce()
{
this.MySocket.Send(Request).To(ResponceSocket);
this.MySocket.Receive(Responce).From(ResponceSocket);
return Responce;
}
bad try help! The second post of code is one we can work with and the way forward.
But you're right! the documentation is not the best.
a source to share
Did you know that you will receive a response to the message you sent? Remove the asynchronous behavior from the socket and just try sending and receiving synchronously (although that might block your thread for now). Once you know this behavior works, edit your question and post this code and I'll help you with the streaming model. After the working part of the network, i.e. Send / receive, works, the streaming model is pretty simple.
a source to share