【问题标题】:Correct use of BeginReceive / EndReceive?正确使用 BeginReceive / EndReceive?
【发布时间】:2014-11-08 02:09:19
【问题描述】:

从套接字异步接收数据 .Net 支持对称的 BeginReceive/EndReceive 调用。基本上,您调用BeginReceive() 开始监听并确定数据到达时应调用的回调。在回调中调用EndReceive() 来提取数据并结束异步读取操作。

我正在编写 C#/.Net 软件来控制一些工业设备。控制面板允许用户设置和初始化设备,一旦它被初始化BeginReceive() 被称为开始监听数据。在回调中EndReceive() 用于提取数据,但我想立即恢复收听,所以我想我应该在完成EndReceive() 之后再次调用BeginReceive()。这是正确的吗?

如果是这样,我是否可以在代码的其他地方使用检查或测试来了解是否已经调用了 BeginReceive(),所以我不会尝试在同一个套接字上连续调用 BeginReceive() 两次,之前EndReceive() 已被调用?

【问题讨论】:

  • 没错。我不知道您是否可以检查它是否已经被调用(除了您自己的自定义标志)。很可能两次调用BeginRecieve 无论如何都不会做任何事情;检查文档。
  • Socket,或者更确切地说是操作系统,会自行检查。如果您尝试在另一个接收已经在进行时调用BeginReceive,它将引发表示套接字错误 WSAEALREADY 的异常(我认为)。毕竟,尝试同时从同一个套接字接收两个缓冲区没有多大意义!但是如果你问Socket 上是否有一个属性告诉你操作是否正在进行,答案是否定的(因为不可避免的竞争条件,它无论如何都没用)。如果需要,您可以实现自己的锁定。
  • 您不应该以会导致重入的方式调用 begin receive .. 这是肯定的。如果您遇到需要一个标志来知道您是否调用它的情况 - 这可能意味着您的架构不正确。
  • @AntonTykhyy:“如果您尝试在另一个接收正在进行时调用 BeginReceive,它将引发异常”这是不正确的。实际上,发出多次接收操作(即多次调用BeginReceive())是处理基于IOCP的I/O(如Socket类)时常用的策略。诀窍是,虽然缓冲区将按照它们通过接收操作提交的顺序填充,但完成事件可能会乱序发生。所以调用者确实需要跟踪他们的缓冲顺序并确保它们被正确处理。
  • 欢迎查看我在 Github 上的 .NET Sockets 存储库,它使用了 BeingReceive/EndReceive 模式。 here 是 C# 版本。

标签: c# sockets


【解决方案1】:

避免在调用EndReceive() 之前再次调用BeginReceive() 的方法是将调用BeginReceive() 放入调用EndReceive() 的完成回调中。当然,唯一不应该在那里执行的对BeginReceive() 的调用是您在Socket 连接后立即进行的调用。

编辑:

需要明确的是:在任何接收完成发生之前,允许调用BeginReceive() 任意次数。但是,当您这样做时,您需要确保进行必要的内务管理,以确保以正确的顺序处理数据(即,您按照通过 BeginReceive() 提交缓冲区的相同顺序处理缓冲区)。

所以上面的答案是关于不必做所有的家务,从而使代码更简单。

如果您以后只在调用EndReceive() 的同一位置向BeginReceive() 进行后续调用,那么让事情井井有条是微不足道的。请注意,您仍然需要正确执行此操作:确保始终以正确顺序接收缓冲区的最简单方法是在您调用 @987654332 之前之后再次调用 BeginReceive() @(但仍然使用相同的方法)。

【讨论】:

  • 只是为了确保我理解您的答案:我认为您的意思是应该对 BeginReceive() 进行两次调用 - 一个是第一次连接套接字的地方,一个是回调/处理程序中的一个。第一个 BeginReceive() 只调用一次,所有后续对 BeginReceive 的调用都来自处理程序,就在 EndReceive() 之后。这是正确的吗?
  • 对于最简单的实现,是的。正如我在问题的 cmets 中提到的,您 可以 多次致电 BeginReceive(),但随后您必须进行内务处理以确保以正确的顺序处理接收到的数据。我将在答案中添加一些说明。
【解决方案2】:

是的,您需要在收到数据后致电BeginReceive,以便在数据可用时获得更多信息。

由您来处理Socket 的状态,您不会调用BeginReceive 两次。有一些属性可以让您知道数据是否可用,但没有任何属性可以告诉您是否已经在等待接收数据。

Socket 的大多数实现最终成为另一个具有此逻辑的包装类,因此如果第二次调用 BeginReceive 将被忽略。 Socket 课程有时会让人感到痛苦,因为您无法在不关闭的情况下取消 BeginReceive

【讨论】:

    猜你喜欢
    • 2018-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-26
    • 2011-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多