C # SocketAsyncEventArgs обрабатывает получение и отправку данных - PullRequest
8 голосов
/ 26 января 2012

Я пытаюсь понять класс 'SocketAsyncEventArgs' в C #. http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx

Я следую этому уроку: http://www.codeproject.com/Articles/83102/C-SocketAsyncEventArgs-High-Performance-Socket-Cod

Теперь я застрял в том, как именно мне следует обрабатывать данные на моем сервере. Я пытаюсь использовать SocketAsyncEventArgs для подключенного клиента с выделенным буферным пространством в 512 байт в BufferManager. Затем я хочу декодировать данные byte [] в свой собственный класс (ClientPacket), который содержит byte [] для декодирования и чтения из.

Это все хорошо, но мой сервер не всегда отвечает данными, вопрос:

Использую ли я 1 SocketAsyncEventArgs для получения и циклический цикл для обработки полученных данных, затем выделяю SocketAsyncEventArgs из пула всякий раз, когда мне нужно отправить, а затем возвращает его по завершении?

А как SocketAsyncEventArgs узнает, когда чтение завершено? (когда он завершается с буфером byte [] до того, как перезаписывает его новыми данными), например, как он возвращается в пул, когда завершается, если я не отвечаю обратно?

1 Ответ

9 голосов
/ 26 января 2012

Я использую только один экземпляр SocketAsyncEventArgs для всех своих нужд. Я просто сбрасываю буфер между каждым запросом (устанавливая его в новый байт []).

Как только я подключился и получил ссылку на сокет, я начинаю слушать так:

public void StartListening(SocketAsyncEventArgs e)
{
    ResetBuffer(e);
    e.Completed += SocketReceive;

    socket.ReceiveAsync(e);
}

У меня есть вспомогательная функция, которая сбрасывает буфер:

private void ResetBuffer(SocketAsyncEventArgs e)
{
    var buffer = new Byte[SocketBufferSize];

    e.SetBuffer(buffer, 0, SocketBufferSize);
}

Я обрабатываю данные как:

private void SocketReceive(Object sender, SocketAsyncEventArgs e)
{
    ProcessData(e.Buffer, 0, e.BytesTransferred);

    ResetBuffer(e);

    socket.ReceiveAsync(e);
}

В ProcessData вы можете использовать байтовый массив по мере необходимости для извлечения данных. Я использую его для создания MemoryStream, который затем десериализуем в свой класс (аналогично ClientPacket) следующим образом:

private void ProcessData(Byte[] data, Int32 count)
{
    using (var stream = new MemoryStream(data, 0, count))
    {
        var serializer = new XmlSerializer(typeof(ClientPacket));

        var packet = serializer.Deserialize(stream);

        // Do something with the packet
    }
}

Что касается вашего последнего вопроса. Каркас обрабатывает все, что связано с базовым протоколом TCP и т. Д., Поэтому вы можете рассчитывать на то, что обработчик событий вызывается всякий раз, когда есть данные для обработки. Используйте значение e.BytesTransferred, чтобы указать объем данных, которые вы фактически получили, который может быть меньше, чем размер буфера, но никогда не будет превышать его (SocketBufferSize в моем коде). Если сообщение было больше размера буфера, инфраструктура TCP будет буферизировать сообщения и отправлять их вам порциями на основе SocketBufferSize (вызывая событие один раз для каждого фрагмента). Если это проблема, просто увеличивайте SocketBufferSize до тех пор, пока большая часть вашего сообщения не будет получена за один блок.

Недостатком блока является то, что сообщения могут объединяться инфраструктурой, что означает, что вам может понадобиться способ узнать, когда закончилось первое сообщение. Типичные подходы включают предварительное добавление к вашему сообщению 4-байтового целого числа, которое указывает длину сообщения. Я могу уточнить, если нужно.

Надеюсь, это поможет.

...