2012-03-06 5 views
1

Я занимаюсь программированием сокетов на C#. Мне нужно создать клиентское приложение, которое связывается с сервером с данным протоколом.Асинхронный сокет клиент получает

Я успешно реализовал асинхронный метод отправки, но у меня возникли проблемы при реализации алгоритма получения. Синхронный метод приема работает нормально.

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

Я построил многопотоковый класс MessageFlow с тремя потоками: один заботится о передаче сообщений, другой заботится о получении сообщений, третий заботится о интерпретировать полученные сообщения и что-то делать.

Функция работника для получающего потока выглядеть следующим образом

private void ReadSocketWorker() 
{ 
    while (this.canRun) 
    { 
    xComClient.Receive(); 
    xComClient.receiveDone.WaitOne(); 
    Thread.Sleep(10); 
    } 
} 

XComClient мой класс, имеющий гнездо и все методы для отправки и получения сообщений.

public void Receive() 
{ 
    try 
    { 
     StateObject state = new StateObject(); 
     state.workSocket = socketClient; 
     socketClient.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state); 
    } 
    catch (Exception e) 
    { 
     throw e; 
    } 
} 

private void ReceiveCallback(IAsyncResult ar) 
     { 
      try 
      { 
       StateObject state = (StateObject)ar.AsyncState; 
       Socket client = state.workSocket; 

       // Read data from the remote device. 
       int iReadBytes = client.EndReceive(ar); 

       if (iReadBytes > state.GetBufferSize()) 
       { 
        byte[] bytesReceived = new byte[iReadBytes]; 
        Buffer.BlockCopy(state.buffer, 0, bytesReceived, 0, iReadBytes); 
        state.responseList.Enqueue(bytesReceived); 
        client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, 
         new AsyncCallback(ReceiveCallback), state); 
       } 
       else 
       { 
        byte[] bytesReceived = new byte[iReadBytes]; 
        Buffer.BlockCopy(state.buffer, 0, bytesReceived, 0, iReadBytes); 
        state.responseList.Enqueue(bytesReceived); 
        BuildReceivedMessage(state); 
        receiveDone.Set(); 
       } 
      } 
      catch (Exception e) 
      { 
       throw e; 
      } 
     } 

public class StateObject 
{ 
    public Socket workSocket = null; 
    public const int BufferSize = 20480; 
    public byte[] buffer = new byte[BufferSize]; 

    public Queue<byte[]> responseList = new Queue<byte[]>(); 

    public int GetBufferSize() 
    { 
     return BufferSize; 
    } 
} 

Что я делаю неправильно?

ответ

1

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

Я действительно надеюсь, что throw e; только в примере. Поскольку он уничтожает трассировку стека (и, следовательно, скрывает, где возникло исключение). Вы можете прочитать мою статью Don't catch that exception и другие мои статьи с тегами exceptions

Приемный метод может выглядеть следующим образом:

void OnReceive(IAsyncResult ar) 
{ 
    AppendInternalReadBuffer(); 
    CheckInternalReadBufferForMessageAndProcessIt(); 
    ReadAgain(); 
} 

Это будет блокировать сервер от обрабатывающ более одного сообщения в то время, на одного клиента. Если вы не хотите этого делать (что усложняет ситуацию), вы можете просто использовать ThreadPool в CheckInternalReadBufferForMessageAndProcessIt.

+0

Спасибо. Я был абсолютно беспорядочен, комбинируя синхронный/асинхронный подход – Francesco

2

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