Отправка данных в порядке с SocketAsyncEventArgs

У меня изначально было состояние гонки при отправке данных, проблема была в том, что я разрешал использовать несколько SocketAsyncEventArgs для отправки данных, но первый пакет не был отправлен полностью до 2-го пакета, это потому, что у меня так, если данные не помещается в буфер, он зацикливается до тех пор, пока все данные не будут отправлены, и первый пакет был больше второго пакета, который является крошечным, поэтому второй пакет отправлялся и доходил до клиента до первого пакета.

Я решил эту проблему, назначив 1 SocketAyncEventArgs открытому соединению, которое будет использоваться для отправки данных, и использовал семафор, чтобы ограничить доступ к нему, и заставил SocketAsyncEventArgs перезвонить после его завершения.

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

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

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

Цени любую помощь!

1 ответ

Решение

Если данные должны храниться в порядке, и вы не хотите блокировать, то вам нужно добавить очередь. Я делаю это, отслеживая на своем объекте состояния, есть ли у нас уже активный асинхронный цикл отправки для этого соединения. После постановки в очередь (которая, очевидно, должна быть синхронизирована), просто проверьте, что происходит:

    public void PromptToSend(NetContext context)
    {
        if(Interlocked.CompareExchange(ref writerCount, 1, 0) == 0)
        { // then **we** are the writer
            context.Handler.StartSending(this);
        }
    }

Вот writerCount количество циклов записи (которое должно быть ровно 1 или 0) в соединении; если их нет, мы начинаем.

мой StartSending пытается прочитать из очереди этого соединения; если он может сделать это, он делает обычный SendAsync так далее:

if (!connection.Socket.SendAsync(args)) SendCompleted(args);

(Обратите внимание, что SendCompleted здесь для случая "синхронизации"; это должно было бы SendCompleted через модель событий для случая "асинхронности"). SendCompleted повторяет этот шаг "dequeue, try send async", очевидно.

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

        if (bufferedLength == 0)
        {  // nothing to do; report this worker as inactive
            Interlocked.Exchange(ref writerCount, 0);
            return 0;
        }

Есть смысл?

Другие вопросы по тегам