Асинхронный сокет клиент получает - PullRequest
1 голос
/ 06 марта 2012

Я имею дело с программированием сокетов в 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;
    }
}

Что я делаю не так?

Ответы [ 2 ]

2 голосов
/ 06 марта 2012

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

1 голос
/ 06 марта 2012

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

Я действительно надеюсь, что throw e; только в примере.Так как он уничтожает трассировку стека (и, следовательно, скрывает, где произошло исключение).Вы можете прочитать мою статью Не перехватывать это исключение и другие мои статьи с тегом исключения

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

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

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

...