tags:

views:

357

answers:

1

We're stuck with using buffers on the SocketAsyncEventArgs object.

With the old socket method we'd cast our state object, like this:

clientState cs = (clientState)asyncResult.AsyncState;

However, the 3.5 framework is different.

With have strings arriving from the client in chunks and we can't seem to work out how the buffers work so we can process an entire string when we find a char3.

Code at the moment:

private void ProcessReceive(SocketAsyncEventArgs e)
{
    string content = string.Empty;

    // Check if the remote host closed the connection.
    if (e.BytesTransferred > 0)
    {
        if (e.SocketError == SocketError.Success)
        {
            Socket s = e.UserToken as Socket;
            //asyncResult.AsyncState;

            Int32 bytesTransferred = e.BytesTransferred;

            // Get the message received from the listener.
            content += Encoding.ASCII.GetString(
                e.Buffer, e.Offset, bytesTransferred);

            if (content.IndexOf(Convert.ToString((char)3)) > -1)
            {
                e.BufferList = null;

                // Increment the count of the total bytes receive by the server
                Interlocked.Add(ref this.totalBytesRead, bytesTransferred);
            }
            else
            {
                content += Encoding.ASCII.GetString(
                    e.Buffer, e.Offset, bytesTransferred);
                ProcessReceive(e);
            }
        }
        else
        {
            this.CloseClientSocket(e);
        }
    }
}
+1  A: 

I'll start by saying I've never worked with .NET 3.5 sockets, so this answer is a bit of an educated guess.

The problem you are having is that you aren't storing the content in some state on each read. So the problems are:
Firstly

  1. You call ProcessReceive: string content = string.Empty;

  2. You append to content: 'content += Encoding.ASCII.GetString(e.Buffer, e.Offset, ew.BytesTransferred);`

  3. You call ProcessReceive Again: string content = string.Empty;
  4. The cycle continues and content always gets set to an empty string.

Secondly
To receive the remaining data you are calling ProcessReceive, this is incorrect. You need to issue another read to the underlying Socket using ReceiveAsync.

I've modified your code using this blog post.

// You'll need some state object, if you don't already have one
class AsyncServerState
{
   public byte[] Buffer = new byte[8192]; // 8K buffer
   public StringBuilder Content = new StringBuilder0; // Place to store the content as it's received
   public SocketAsyncEventArgs ReadEventArgs = new SocketAsyncEventArgs();
   public Socket Client;
}

// You'll need to setup the state where ever you process your connect
// something similar to this.
void Accept_Completed(object sender, SocketAsyncEventArgs e)
{
    if (e.SocketError == SocketError.Success)
    {
        Socket client = e.AcceptSocket;
        AsyncServerState state = new AsyncServerState();
        state.ReadEventArgs.AcceptSocket = client;
        state.ReadEventArgs.Completed += new EventHandler(                                              IO_Completed);
        state.ReadEventArgs.UserToken = state;
        state.Client = client;
        state.ReadEventArgs.SetBuffer(state.Buffer, 0, state.Buffer.Length);

        if (!client.ReceiveAsync(state.ReadEventArgs))
        {   
            // Call completed synchonously
            ProcessReceive(state.ReadEventArgs);
        }
    }
    ProcessAccept(e);
}

private void ProcessReceive(SocketAsyncEventArgs e)
{        
    var state = e.UserToken as AsyncServerState;

    // Check if the remote host closed the connection.
    if (e.BytesTransferred > 0)
    {
        if (e.SocketError == SocketError.Success)
        {
            // Get the message received from the listener.
            sting content = Encoding.ASCII.GetString(state.Buffer, 0, e.BytesTransferred);

            // Append the received data to our state
            state.Content.Append(content);                          

            // Increment the count of the total bytes receive by the server
            Interlocked.Add(ref this.totalBytesRead, bytesTransferred);

            if (content.IndexOf(Convert.ToString((char)3)) > -1)
            {
                // Final Message stored in our state
                string finalContent = state.Content.ToString();
                return;                
            }
            else
            {
                // You need to issue another ReceiveAsync, you can't just call ProcessReceive again
                if (!state.Client.ReceiveAsync(state.ReadEventArgs))
                {
                    // Call completed synchonously
                    ProcessReceive(state.ReadEventArgs);
                }                
            }
        }
        else
        {
            this.CloseClientSocket(e);
        }
    }
}
ParmesanCodice