Асинхронные сокеты в C # - PullRequest
       17

Асинхронные сокеты в C #

3 голосов
/ 27 марта 2010

Я не совсем понимаю, как правильно использовать асинхронные методы сокетов в C #. Я буду ссылаться на эти две статьи, чтобы объяснить вещи и задать свои вопросы: Статья MSDN об асинхронных клиентских сокетах и devarticles.com Статья о программировании сокетов .

Мой вопрос о методе BeginReceive(). В статье MSDN эти две функции используются для обработки принимаемых данных:

private static void Receive(Socket client) 
{
  try 
  {
    // Create the state object.
    StateObject state = new StateObject();
    state.workSocket = client;

    // Begin receiving the data from the remote device.
    client.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,
        new AsyncCallback(ReceiveCallback), state);
  } 
  catch (Exception e) 
  {
    Console.WriteLine(e.ToString());
  }
}

private static void ReceiveCallback( IAsyncResult ar ) 
{
  try 
  {
     // Retrieve the state object and the client socket 
     // from the asynchronous state object.
     StateObject state = (StateObject) ar.AsyncState;
     Socket client = state.workSocket;
     // Read data from the remote device.
     int bytesRead = client.EndReceive(ar);
     if (bytesRead > 0) 
     {
         // There might be more data, so store the data received so far.
        state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
         //  Get the rest of the data.
        client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,
            new AsyncCallback(ReceiveCallback), state);
     } 
     else 
     {
        // All the data has arrived; put it in response.
       if (state.sb.Length > 1) 
       {
         response = state.sb.ToString();
       }
       // Signal that all bytes have been received.
       receiveDone.Set();
     }
   } 
   catch (Exception e) 
   {
     Console.WriteLine(e.ToString());
   }
 }

В то время как учебник devarticles.com передает null для последнего параметра метода BeginReceive и продолжает объяснять, что последний параметр полезен, когда мы имеем дело с несколькими сокетами. Теперь мои вопросы:

  1. Какой смысл передавать состояние методу BeginReceive, если мы работаем только с одним сокетом? Это чтобы избежать использования поля класса? Кажется, что делать это бессмысленно, но, может быть, я что-то упускаю.

  2. Как может помочь параметр состояния при работе с несколькими сокетами? Если я звоню client.BeginReceive(...), все ли данные будут прочитаны из сокета client? Учебное пособие devarticles.com звучит так: m_asynResult = m_socClient.BeginReceive (theSocPkt.dataBuffer,0,theSocPkt.dataBuffer.Length, SocketFlags.None,pfnCallBack,theSocPkt);

Данные будут считываться из гнезда theSocPkt.thisSocket, а не из гнезда m_socClient. В их примере это одно и то же, но что будет, если это не так?

Я просто не понимаю, где последний аргумент полезен или, по крайней мере, как он помогает с несколькими сокетами. Если у меня есть несколько сокетов, мне все равно нужно вызывать BeginReceive для каждого из них, верно?

Ответы [ 2 ]

2 голосов
/ 27 марта 2010

Какой смысл передавать состояние методу BeginReceive, если мы работаем только с одним сокетом? Это чтобы избежать использования поля класса? Кажется, что делать это бессмысленно, но, может быть, я что-то упускаю.

Вы правы, если бы вы не использовали штат, вам пришлось бы вместо этого использовать участника. Но это менее локально, чем переменная состояния. Чем больше локальных элементов, тем меньше вероятность их поломки при внесении изменений в другие части кода.

Сравните это с обычными вызовами методов. Почему бы нам просто не установить параметры в качестве членов, а затем вызвать все функции без каких-либо параметров? Это сработало бы ... но было бы ужасно читать код. Сохраняя область действия как можно более локальной, это облегчает понимание и изменение дизайна. Улучшенная инкапсуляция приводит к более надежному коду.

То же самое применимо и здесь. Если у вас есть только один асинхронный обратный вызов, то вы, вероятно, можете просто обойтись без установки члена в классе, но если у вас будет много таких вызовов, эта стратегия вскоре приведет к аналогичным проблемам, как описано выше - запутанный и хрупкий код.

Как может помочь параметр состояния при работе с несколькими сокетами?

Вы можете передавать различные объекты состояния для каждого вызова, каждый из которых содержит свой собственный объект клиента. Обратите внимание, что клиент выбирается из состояния, а не из переменной-члена:

//Socket client = this.client; // Don't do this.
Socket client = state.workSocket; 

Если вы заметили, что все другие методы в документации MSDN принимают Client в качестве параметра. Состояние - это просто способ передачи параметров, поскольку сигнатура метода фиксирована.


Обновление. Относительно вашего вопроса в комментариях .NET проверяет, что вы используете правильный клиентский объект и, если нет, выдает ArgumentException. Из декомпиляции EndReceive в .NET Reflector мы видим это:

if ((result == null) || (result.AsyncObject != this))
{
    throw new ArgumentException(SR.GetString("net_io_invalidasyncresult"), "asyncResult");
}
1 голос
/ 28 марта 2010

Как может помочь параметр состояния, когда иметь дело с несколькими сокетами?

Я думаю, что недоразумение заключается в том, для чего конкретно нужен параметр состояния; при звонке:

client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
    ReceiveCallback, state);

Конечно, на сайте, где вы делаете вызов, ясно, на каком «клиенте» сделан вызов, но когда вызывается метод ReceiveCallback, этот метод не может узнать клиента. Обратите внимание, что многие приемы в разных сокетах могут отправляться одновременно, но все они используют один и тот же обработчик обратного вызова для обработки результатов.

При передаче данных в параметр state у вас есть возможность передать дополнительный контекст обратному вызову, чтобы он мог выяснить (в данном случае), с какого сокета был получен прием.

Если вы передадите «неправильное значение» в параметре состояния, то обратный вызов, очевидно, ничего не сможет сделать, чтобы защитить вас ... последствия зависят от того, что вы делаете с данными в состоянии. В лучшем случае это не может иметь значения, если состояние на самом деле не используется, в худшем случае данные могут быть обработаны так, как если бы они поступили из неверного сокета, и вы можете в итоге выполнить команду «удалить все сообщения» в контексте учетной записи, которая не сделала запрос. Но это ничем не отличается от любой другой программной ошибки;)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...