views:

797

answers:

4

Hi, I'm struggling a bit with socket programming (something I'm not at all familiar with) and I can't find anything which helps from google or MSDN (awful). Apologies for the length of this.

Basically I have an existing service which recieves and responds to requests over UDP. I can't change this at all.

I also have a client within my webapp which dispatches and listens for responses to that service. The existing client I've been given is a singleton which 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 array. All kinds of things about this seem wrong to me and the infinite thread breaks my unit testing so I'm trying to replace this service with one which makes it's it's send/receives asynchronously instead.

Point 1: Is this the right approach? I want a non-blocking, scalable, thread-safe service.

My first attempt is roughly like this, which sort of worked but the data I got back was always shorter than expected (i.e. the buffer did not have the number of bytes requested) and seemed to throw exceptions when processed.

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 trawling and this recursive pattern I frequently saw, but this version always times out! 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: So clearly I'm getting something quite wrong.

Point 3: I'm not sure if I'm going about this right. How does the data coming from the remote service even get to the right listening thread? Do I need to create a socket per request?

Out of my comfort zone here. Need help.

A: 

Not the solution for you, just a suggestion - come up with the simplest code that works peeling off all the threading/events/etc. From there start adding needed, and only needed, complexity. My experience always was that in the process I'd find what I was doing wrong.

Nikolai N Fetissov
trouble is, that's how I got this far :)
A: 

So is your program SUDO outline as follows?

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

ill try help! The second code post is the one we can work with and the way forward.

But you are right! the documentation is not the best.

divinci
A: 

Do you know for sure that you get a response to the message you send? Remove the asynchronous behavior from the socket and just try to send and receive synchronously (even though this may block your thread for now). Once you know this behavior is working, edit your question and post that code, and I'll help you with the threading model. Once networking portion, i.e., the send/receive, is working, the threading model is pretty straightforward.

Matt Davis
A: 

One POSSIBLE issue is if your send operation goes to the server, and it responds before windows sets up the asynchronous listener. If you arent listening the data wont be accepted on your side (unlike TCP) Try calling beginread before the send operation.

MKA