Подтвердить что ты не робот

TcpClient vs Socket при работе с асинхронностью

Это еще не TcpClient vs Socket.

TcpClient - это оболочка класса Socket для упрощения разработки, а также раскрытия лежащего в основе Socket.

еще...

На странице библиотеки MSDN для класса TcpClient можно прочитать следующее замечание:

Класс TcpClient предоставляет простые способы подключения, отправки, и приема данных потока по сети в режиме синхронной блокировки.

И для класса Socket:

Класс Socket позволяет выполнять как синхронные, так и асинхронная передача данных с использованием любого из протоколов связи перечисленные в перечислении ProtocolType.

Чтобы отправлять/получать некоторые данные асинхронно только через TcpCient, необходимо выполнить вызов GetStream, чтобы извлечь базовый сетевой поток из/из которого данные можно читать/записывать асинхронно, вызывая методы ReadAsync и WriteAsync на нем, следуя шаблон TAP (потенциально используя конструкции async/await).

Чтобы отправлять/получать некоторые данные асинхронно через Socket (я не эксперт, но я думаю, что все правильно), мы можем напрямую читать/записывать/из самого экземпляра сокета, вызывая BeginRead/EndRead BeginWrite/EndWrite (или просто ReadAsync или WriteAsync.. не подвергая шаблон TAP - то есть не возвращая Задачу.. запутывая).

Прежде всего, любая идея, почему класс Socket в .NET 4.5 каким-либо образом не реализует шаблон TAP, т.е. ReadAsync и WriteAsync возвращают Task (событие, если вызываются по-разному, чтобы сохранить обратную совместимость)?

В любом случае, достаточно легко создать метод Task из пары методов модели APM, поэтому позвольте сказать, что я называю этот асинхронный метод (для чтения) ReadAsyncTAP (возвращающий задачу).

Хорошо? Так что теперь позвольте сказать, что я хочу закодировать клиентский метод async Task<Byte[]> ReadNbBytes(int nbBytes), который я вызову из моего кода, чтобы асинхронно читать определенное количество байтов из Сети.

Реализация этого метода, основанного исключительно на TcpClient, получит NetworkStream, вызвав GetStream и будет содержать асинхронный цикл, ожидающий вызова ReadAsync, до тех пор, пока не будет заполнен буфер.

Реализация этого метода на основе Socket будет содержать асинхронный цикл, ожидающий ReadAsyncTAP, пока буфер не будет заполнен.

В конце дня, с точки зрения клиентского кода, я полагаю, что это не имеет значения. В обоих случаях вызов await ReadNbBytes будет немедленно возвращаться. Однако, я полагаю, это имеет значение за кулисами... Для TcpClient, полагаясь на NetworkStream, считывание каким-то образом блокируется или нет в любой точке, по сравнению с прямым использованием сокета? Если это не замечание, сделанное для TcpClient, неверно, когда речь идет о синхронном режиме блокировки?

Было бы очень признательно, если бы кто-нибудь мог прояснить!

Спасибо.

4b9b3361

Ответ 1

Асинхронный ввод-вывод в потоках TcpClient не блокируется. Похоже, что документы MSDN ошибочны (вы можете проверить это в Reflector, выполнив вызовы NetworkStream async I/O).

Stream типы "интересны": по умолчанию базовый класс Stream будет реализовывать асинхронный ввод-вывод, блокируя поток пула потоков на синхронном вводе-выводе. Таким образом, вы никогда не хотите делать асинхронный ввод-вывод на чем-то вроде MemoryStream, который предоставляет только синхронные методы.

NetworkStream обеспечивает асинхронный ввод-вывод, поэтому асинхронный ввод-вывод в экземплярах NetworkStream фактически является асинхронным. Но это не всегда так: FileStream в частности обычно не асинхронный, но это если вы построите экземпляр как раз справа.

Относительно того, почему Socket не имеет методов TAP: это очень хороший вопрос! Я предположил, что это был надзор, но теперь, когда .NET 4.5 выпущен, похоже, что это было запрещено. Возможно, они просто не хотят перекомпилировать API. Socket уже имеет синхронные и два асинхронных API, охватывающих один и тот же набор операций (Send, SendTo, Receive, ReceiveFrom, Connect, Accept, Disconnect). TAP, в свою очередь, потребует двух дополнительных асинхронных API для этого полного набора. Это, по крайней мере, вызовет интересную ситуацию именования (имена *Async уже заняты, и они будут добавлять еще два имени *Async для каждой операции).

Боковое примечание: "дополнительные" API предназначены для высокопроизводительной асинхронной связи Socket. Они используют SocketAsyncEventArgs, который не так прост в использовании, но уменьшает количество мусора памяти. Если TAP API были добавлены в Socket, они хотели бы предоставить как простые в использовании версии (wrapping Begin/End), так и более высокопроизводительные версии (wrapping Async).

Если вам интересно создавать методы TAP для Socket, хорошей отправной точкой является Stephen Toub Ожидание операций сокета (он предоставляет только обертки для высокопроизводительного API). Я использую что-то подобное для моих сокетов Async -enabled.