DannyQiu
DannyQiu

Reputation: 31

How to use Socket.SendAsync to send large data

private void ProcessReceive(SocketAsyncEventArgs e)
{
    // Check if the remote host closed the connection.
    if (e.BytesTransferred > 0)
    {
        if (e.SocketError == SocketError.Success)
        {
            Token token = e.UserToken as Token;
            token.SetData(e);

            Socket s = token.Connection;
            if (s.Available == 0)
            {
                Boolean willRaiseEvent = false;
                // GET DATA TO SEND
                byte[] sendBuffer = token.GetRetBuffer();
                // this.bufferSize IS SocketAsyncEventArgs buffer SIZE
                byte[] tempBuffer = new byte[this.bufferSize];
                int offset = 0;
                int size = (int)Math.Ceiling((double)sendBuffer.Length / (double)this.bufferSize);
                for (int i = 0; i < size - 1; i++)
                {
                    Array.Clear(tempBuffer, 0, this.bufferSize);
                    Array.Copy(sendBuffer, offset, tempBuffer, 0, this.bufferSize);
                    e.SetBuffer(tempBuffer, 0, this.bufferSize);
                    willRaiseEvent = s.SendAsync(e);
                    offset += this.bufferSize;
                }
                int remainSize = sendBuffer.Length - this.bufferSize * (size - 1);
                Array.Clear(tempBuffer, 0, this.bufferSize);
                Array.Copy(sendBuffer, offset, tempBuffer, 0, remainSize);
                e.SetBuffer(tempBuffer, 0, remainSize);
                willRaiseEvent = s.SendAsync(e);

                if (!willRaiseEvent)
                {
                    this.ProcessSend(e);
                }
            }
            else if (!s.ReceiveAsync(e))
            {
                // Read the next block of data sent by client.
                this.ProcessReceive(e);
            }
        }
        else
        {
            this.ProcessError(e);
        }
    }
    else
    {
        this.CloseClientSocket(e);
    }
}

This code is modified from MSDN

Why in circulation, execute s.SendAsync(e) the second time, it will be error

Exception:An asynchronous socket operation is already in progress using this SocketAsyncEventArgs instance

How can I send large data ?

Upvotes: 3

Views: 24939

Answers (3)

e1715412
e1715412

Reputation: 11

The problem is not that you have to wait until Completed is raised. I think, it is also not the purpose of async programing to wait for any event.

But the SocketAsyncEventArgs can only be reused if the last action is completed. So, you can resolve the issue just by create a new SocketAsyncEventArgs within each loop.

Upvotes: 1

Kiril
Kiril

Reputation: 40395

You have to wait for the Completed event to be raised before you can make another asynchronous send. Don't forget to add your own event handler in order for you to get the callback:

e.Completed += new EventHandler<SocketAsyncEventArgs>(SendCallback);

You can use my example of an Asynchronous HTTP client to model yours:

private void BeginSend()
{
    _clientState = EClientState.Sending;
    byte[] buffer = GetSomeData(); // gives you data for the buffer

    SocketAsyncEventArgs e = new SocketAsyncEventArgs();
    e.SetBuffer(buffer, 0, buffer.Length);
    e.Completed += new EventHandler<SocketAsyncEventArgs>(SendCallback);

    bool completedAsync = false;

    try
    {
        completedAsync = _socket.SendAsync(e);
    }
    catch (SocketException se)
    {
        Console.WriteLine("Socket Exception: " + se.ErrorCode + " Message: " + se.Message);
    }

    if (!completedAsync)
    {
        // The call completed synchronously so invoke the callback ourselves
        SendCallback(this, e);
    }

}

Here is the callback method:

private void SendCallback(object sender, SocketAsyncEventArgs e)
{
    if (e.SocketError == SocketError.Success)
    {
        // You may need to specify some type of state and 
        // pass it into the BeginSend method so you don't start
        // sending from scratch
        BeginSend();
    }
    else
    {
        Console.WriteLine("Socket Error: {0} when sending to {1}",
               e.SocketError,
               _asyncTask.Host);
    }
}

After the callback is complete, you can make another call to BeginSend again, until you're done sending data.

Upvotes: 7

theWalker
theWalker

Reputation: 2020

Another solution: you may use blocked socket, this return after completition or error, but it must be in another thread.

Upvotes: 0

Related Questions