2014-11-07 3 views
4

Для асинхронного приема данных из сокета .Net поддерживает симметричные вызовы BeginReceive/EndReceive. В основном вы вызываете BeginReceive(), чтобы начать прослушивание и идентифицировать обратный вызов, который должен вызываться, когда поступают данные. Внутри обратного вызова вы вызываете EndReceive() для извлечения данных и завершения асинхронной операции чтения.Правильное использование BeginReceive/EndReceive?

Я пишу C# /. Чистое программное обеспечение для управления некоторым промышленным оборудованием. Панель управления позволяет пользователям настраивать и инициализировать оборудование, и после его инициализации BeginReceive() называется началом прослушивания данных. В обратном вызове EndReceive() используется для извлечения данных, но я хочу возобновить прослушивание сразу, поэтому я думаю, что я должен позвонить BeginReceive() снова сразу после выполнения EndReceive(). Это верно?

Если да, то есть проверка или тест, который я могу использовать в другом месте в коде, чтобы узнать, является ли BeginReceive() уже назвали, так что я не пытаюсь вызвать BeginReceive() два раза подряд на одной и той же розетке, перед тем EndReceive() было называется?

+0

Это правильно. Я не знаю, можете ли вы проверить, был ли он уже вызван (кроме вашего собственного настраиваемого флага). Вполне возможно, что два вызова 'BeginRecieve' ничего не сделают; проверьте документацию. – BradleyDotNET

+0

'Socket', или, скорее, ОС, проверяет это самостоятельно.Если вы попытаетесь вызвать 'BeginReceive', пока другой прием уже выполняется, он выдает исключение, представляющее ошибку сокета WSAEALREADY (я думаю). В конце концов, нет смысла пытаться получать в два буфера из одного и того же сокета одновременно! Но если вы спросите, есть ли свойство на 'Socket', которое сообщает вам, выполняется ли операция, ответ - нет (это было бы бесполезно в любом случае из-за неизбежного состояния гонки). Вы можете реализовать свою собственную блокировку, если вам нужно. –

+0

Вы не должны называть начало приема способом, который может привести к повторному включению. Это точно. если вы столкнулись с ситуацией, когда вам нужен флаг, чтобы узнать, вызвали вы его или нет, это, вероятно, означает, что ваша архитектура неверна. –

ответ

6

Как вы избежать вызова BeginReceive() снова перед вызовом EndReceive() должен поместить вызов BeginReceive() в функции обратного вызова завершения, где вы звоните EndReceive(). Единственный вызов BeginReceive(), который не должен быть выполнен, конечно, тот, который вы делаете сразу после подключения Socket.

EDIT:

Чтобы было ясно: разрешено называть BeginReceive() любое количество раз до того, как получить завершение происходит. Но когда вы это делаете, вам нужно убедиться, что вы выполняете необходимую уборку, чтобы обеспечить обработку данных в правильном порядке (т. Е. Обрабатываете буферы в том же порядке, в котором вы отправили их через BeginReceive()).

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

Если вы только делаете свои последующие звонки в BeginReceive() в том же месте, где вы звоните EndReceive(), тривиально держать вещи в порядке. Обратите внимание, что вам все равно нужно сделать это правильно: самый простой способ гарантировать, что вы всегда получаете буферы в правильном порядке, - это просто не звонить BeginReceive() еще до после, вы вызвали EndReceive() (но все же в том же методе) ,

+0

Просто, чтобы убедиться, что я понимаю ваш ответ: я думаю, вы говорите, что должно быть ровно два звонка в BeginReceive() - тот, где сокет сначала подключен, и один в обратном вызове/обработчике. Первый BeginReceive() вызывается только один раз, все последующие вызовы BeginReceive происходят из обработчика сразу после EndReceive(). Это верно? – user316117

+0

Для простейшей реализации да. Как я уже упоминал в комментариях к вопросу, вы _can_ вызываете 'BeginReceive()' несколько раз, но тогда вам нужно вести домашнее хозяйство, чтобы убедиться, что вы обработали полученные данные в правильном порядке. Я добавлю некоторые пояснения к ответу. –

2

Да, вам необходимо позвонить BeginReceive после того, как вы закончите получать данные, чтобы получить больше по дороге, когда данные станут доступными.

Это зависит от вас, чтобы обработать состояние Socket, где вы не звоните BeginReceive дважды. Доступны свойства, позволяющие вам знать, доступны ли данные, но ничего не говорит о том, что вы уже ожидаете получения данных.

Большинство реализаций Socket в конечном итоге являются еще одним классом-оболочкой, который имеет эту логику на месте, так что BeginReceive игнорируется, если вызывается второй раз. Класс Socket - это боль от времени, так как вы не можете отменить BeginReceive без закрытия.

Смежные вопросы