Как использовать Socket.SendAsync для отправки больших данных - PullRequest
3 голосов
/ 07 февраля 2012
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);
    }
}

Этот код изменен с MSDN

Почему в обращении выполнить s.SendAsync(e) во второй раз, это будет ошибка

Исключение: асинхронная операция сокетауже выполняется с использованием этого экземпляра SocketAsyncEventArgs

Как отправить большие данные?

Ответы [ 3 ]

7 голосов
/ 07 февраля 2012

Вам нужно дождаться события Completed, прежде чем вы сможете сделать еще одну асинхронную отправку.Не забудьте добавить собственный обработчик событий, чтобы вы могли получить обратный вызов:

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

Вы можете использовать мой пример асинхронного HTTP-клиента для моделирования вашего:

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

}

Вот метод обратного вызова:

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

После завершения обратного вызова вы можете сделать еще один вызов на BeginSend еще раз, пока не закончите отправку данных.

1 голос
/ 19 апреля 2013

Проблема не в том, что вам нужно ждать, пока Completed не будет поднят. Я думаю, что это также не цель асинхронного программирования для ожидания какого-либо события.

Но SocketAsyncEventArgs можно использовать повторно только в том случае, если последнее действие завершено. Таким образом, вы можете решить эту проблему, просто создав новый SocketAsyncEventArgs в каждом цикле.

0 голосов
/ 07 февраля 2012

Другое решение: вы можете использовать заблокированный сокет, этот возврат после завершения или ошибки, но он должен быть в другом потоке.

...