Документация NetworkStream.Read подразумевает, что он неблокируемый («Если данные не доступны для чтения, метод Read возвращает 0»), поэтому я предполагаю, что мне не нужны асинхронные обратные вызовы для чтения ..да?
Я не понимаю, как это подразумевает неблокирующую операцию, если только вы никогда не планируете, что будут доступны какие-либо данные для чтения.Если есть данные, доступные для чтения, операция будет заблокирована на столько времени, сколько потребуется для чтения данных.
Но как насчет записи?Ну, у Microsoft есть одно из обычных низкокачественных учебных пособий по этому вопросу, и они предлагают написать громоздкий метод AsyncCallback для каждой операции.Какой смысл кода внутри этого обратного вызова?
Смысл в том, чтобы не писать для себя еще более громоздкий код управления потоками.Поскольку многопоточность идет, AsyncCallback примерно так же прост, как он получает, хотя этот пример довольно глупый.В этом примере подпрограмма обратного вызова сигнализирует ManualResetEvent
(sendDone.Set()
), что будет одним из способов избежать опроса;у вас будет отдельный поток, ожидающий этого ManualResetEvent
, который блокирует его до тех пор, пока что-то еще не вызовет его метод Set()
.Но подождите, разве не было главной целью использования AsyncCallback, чтобы избежать написания собственного управления потоками?Так зачем же нить, которая ждет на ManualResetEvent
?Лучший пример показал бы, что вы на самом деле делаете что-то в вашем методе AsyncCallback, но имейте в виду, что если вы взаимодействуете с какими-либо элементами пользовательского интерфейса, такими как элементы управления Forms, вам нужно будет использовать метод Invoke
элемента управления, а не управлять им напрямую.
Если операция уже завершена, зачем нам вызывать Socket.EndSend (IAsyncResult)?
С документация MSDN для Stream.BeginWrite
Method :
Передать IAsyncResult, возвращенный текущим методом, в EndWrite, чтобы гарантировать, что запись завершает и освобождает ресурсы соответствующим образом.EndWrite должен вызываться один раз для каждого вызова BeginWrite.Вы можете сделать это либо с помощью того же кода, который называется BeginWrite, либо с помощью обратного вызова, переданного BeginWrite.Если во время асинхронной записи возникает ошибка, исключение не будет выдано, пока EndWrite не будет вызван с IAsyncResult, возвращенным этим методом.
Вы никогда не знаете, что произойдет с операциями ввода-вывода, иВы не можете выполнять обычную обработку ошибок с асинхронной операцией, потому что выполнение происходит в отдельном потоке в пуле потоков.Таким образом, смысл вызова EndWrite
состоит в том, чтобы дать вам возможность обрабатывать любые ошибки, помещая EndWrite
в блок try / catch внутри вашего метода AsyncCallback.
Не могу япросто храните где-нибудь IAsyncResult ... и периодически проверяйте, завершен ли он?
Можно, если вы хотите обработать все проблемы управления потоками и безопасности, чтобы гарантировать, что разные потоки никогда не будут пытаться обработатьто же самое IAsyncResult в то же время, но это кажется мне гораздо более сложной задачей, чем написание AsyncCallback.Опять же, смысл использования асинхронных методов - сделать так, чтобы вам приходилось как можно меньше заниматься проблемами потоков.
Другая проблема.Сообщения, которые я отправляю, разбиты на массив заголовков и массив тел.Могу ли я выпустить два BeginWrites подряд?
Хотя я никогда не пробовал этого, я не знаю, сработает ли это, и это действительно зависит от конкретной реализации класса NetworkStream.Возможно, 2-й запрос подождет, пока 1-й запрос не завершится, прежде чем попытаться выполнить, но у меня есть ощущение, что он либо сгенерирует исключение, либо вы получите поврежденные данные на другом конце.Если вы хотите отправить 2 отдельных потока данных одновременно, я думаю, что лучший способ сделать это - использовать 2 отдельных NetworkStreams.