7

I am used to sync sockets and had a few headaches to get to the point where I am now, especially with Socket.Receive(..) not always receiveing all bytes

Here is my code what I used to use

    public byte[] Receive(int size)
    {
        var buffer = new byte[size];
        var r = 0;
        do
        {
            // ReSharper disable once InconsistentlySynchronizedField
            var c = _clientSocket.Receive(buffer, r, size - r, SocketFlags.None);
            if (c == 0)
            {
                throw new SocketExtendedException();
            }
            r += c;
        } while (r != buffer.Length);
        return buffer;
    }

Now I started to use sockets in Windows Phone BUT .Receive(..) is not available and I managed to get Socket.ReceiveAsync(..) working but I am concerned (no problems happened so far) here is my new code, I have not implemented the checking if all bytes has been recieved or not nor do I know if I have to with the following code

    private byte[] ReadBySize(int size = 4)
    {
        var readEvent = new AutoResetEvent(false);
        var buffer = new byte[size];
        var recieveArgs = new SocketAsyncEventArgs()
        {
            UserToken = readEvent
        };
        recieveArgs.SetBuffer(buffer, 0, size);
        recieveArgs.Completed += recieveArgs_Completed;
        _connecter.ReceiveAsync(recieveArgs);
        readEvent.WaitOne();

        if (recieveArgs.BytesTransferred == 0)
        {
            if (recieveArgs.SocketError != SocketError.Success)
                throw new SocketException((int)recieveArgs.SocketError);
            throw new CommunicationException();
        }
        return buffer;
    }

    void recieveArgs_Completed(object sender, SocketAsyncEventArgs e)
    {
        var are = (AutoResetEvent)e.UserToken;
        are.Set();
    }

This is my first use of ReceiveAsync can someone point out anything I might have done wrong or need to change

6
  • I would suggest using BeginReceive() Commented Aug 24, 2015 at 7:42
  • In windows Phone BeginRecieve and Recieve is not available, I have used BeginReceive aswell but its not available, I can only use StreamSocket or Socket (with RecieveAsync) Commented Aug 24, 2015 at 7:42
  • Yeah sorry my bad, did not read it was windows phone. Commented Aug 24, 2015 at 7:43
  • Are you using TCP? Have you considered using TcpClient/TcpListener if that is the case? NetworkStream is a lot easier to work with. Commented Aug 24, 2015 at 7:47
  • unfortunatly TcpClient also not available in Windows Phone Commented Aug 24, 2015 at 8:20

2 Answers 2

3

Ok I went and took a large buffer and send it in batches with a sleep interval in between to replicate 'not all bytes received' So my code above doesn't recieve all bytes. for those who also use ReceiveAsync(..) here is my code that works

    private byte[] ReadBySize(int size = 4)
    {
        var readEvent = new AutoResetEvent(false);
        var buffer = new byte[size]; //Receive buffer
        var totalRecieved = 0;
        do
        {
            var recieveArgs = new SocketAsyncEventArgs()
            {
                UserToken = readEvent
            };
            recieveArgs.SetBuffer(buffer, totalRecieved, size - totalRecieved);//Receive bytes from x to total - x, x is the number of bytes already recieved
            recieveArgs.Completed += recieveArgs_Completed;
            _connecter.ReceiveAsync(recieveArgs);
            readEvent.WaitOne();//Wait for recieve

            if (recieveArgs.BytesTransferred == 0)//If now bytes are recieved then there is an error
            {
                if (recieveArgs.SocketError != SocketError.Success)
                    throw new ReadException(ReadExceptionCode.UnexpectedDisconnect,"Unexpected Disconnect");
                throw new ReadException(ReadExceptionCode.DisconnectGracefully);
            }
            totalRecieved += recieveArgs.BytesTransferred;

        } while (totalRecieved != size);//Check if all bytes has been received
        return buffer;
    }

    void recieveArgs_Completed(object sender, SocketAsyncEventArgs e)
    {
        var are = (AutoResetEvent)e.UserToken;
        are.Set();
    }

The way I work with my Socket applications is to send a Buffer that consist of some variables

[0] -> 0,1,2 0 is keep alive, 1 means there are data, 2 means a type off error occured
[1,2,3,4] size of the actual buffer I am sending
[x(size of 1,2,3,4)] the actual 'Serialized' data buffer
Sign up to request clarification or add additional context in comments.

5 Comments

Why are you even using async APIs if you're effectively making them synchronous by calling ReceiveAsync then WaitOne in the same thread? You could just call the synchronous, blocking Receive().
Because this was 2 years ago and it was windows phone, windows phone does not have Recieve() if you've read the question you would have seen that. I needed it synchronous at that time. Today the code looks a lot different
Thank you, I did indeed miss that part. I would have never guessed that the sync api would be unavailable.
:) My code now contains TaskCompletionSource<byte[]>(), getting rid of the ugly AutoResetEvent and I now make use of async/await, byte[] has become Task<byte[]> and return buffer has becomereturn tcs.Task;, recieveArgs_Completed also changed to accomodate TaskCompletionSource
@DonaldJansen could you update your answer to your latest comment? Would be great to see an async/await implementation! 😊
0

You could create a socket extension like:

public static Task<int> ReceiveAsync(this Socket socket,
    byte[] buffer, int offset, int size, SocketFlags socketFlags)
{
    if (socket == null) throw new ArgumentNullException(nameof(socket));

    var tcs = new TaskCompletionSource<int>();
    socket.BeginReceive(buffer, offset, size, socketFlags, ar =>
    {
        try { tcs.TrySetResult(socket.EndReceive(ar)); }
        catch (Exception e) { tcs.TrySetException(e); }
    }, state: null);
    return tcs.Task;
}

And then a method to read the size you want like this:

public static async Task<byte[]> ReadFixed(Socket socket, int bufferSize)
{
    byte[] ret = new byte[bufferSize];
    for (int read = 0; read < bufferSize; read += await socket.ReceiveAsync(ret, read, ret.Length - read, SocketFlags.None)) ;
    return ret;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.