【问题标题】:How to prevent WSASend() from blocking my UI thread?如何防止 WSASend() 阻塞我的 UI 线程?
【发布时间】:2015-04-24 02:26:12
【问题描述】:

我想使用带有完成例程的重叠 I/O 来处理客户端连接。

在我的UI线程中我想使用WSASend(),但是为了让系统调用我的回调函数通知我数据已经发送,UI线程必须处于等待状态,但这会冻结我的用户界面!

我应该如何解决这个问题?

【问题讨论】:

  • 不要在主线程上这样做
  • @David Heffernan 所以我应该创建另一个调用WSASend() 的线程,当我的 UI 线程想要发送数据时,它应该“告诉”另一个线程并给它发送缓冲区?当然这意味着回调函数将在另一个线程的上下文中被调用。
  • 这是最简单的方法。您也可以使用功能更强大的消息循环:wrongbananas.net/cpp/2006.09.13_win32_msg_loops.html 我个人认为,如果可能的话,我通常更愿意将其隔离到不同的线程上
  • 如果我想接收数据,我还应该“告诉”另一个线程调用WSARecv() 并提供它所需的所有信息:缓冲区 - 套接字句柄 - 等等),是吗对吗?
  • 我不确定。对不起。

标签: c++ windows sockets winapi network-programming


【解决方案1】:

我同意@DavidHeffernan - UI 线程应该做 UI 的事情。 IO 线程肯定需要绑定和端口(服务器),或对等地址和端口(客户端)。来自 ConnectEx 或 AcceptEx 的套接字肯定会更好地加载到 IO 线程中,但是肯定可以在 UI 线程中创建一个带有(此时未定义)套接字成员的 Socket 类,并将其发送给 IO 线程进行处理。缓冲区是构成 Socket 类的一部分,还是单独的 Buffer 类,是设计考虑因素。

一个实现,(我已经成功使用):

设计/定义“线程间通信”(ITC)消息类。这有一个“命令”枚举成员,可以告诉其他线程做一些事情,以及此类消息中可能需要的任何其他有用的东西

从 ITC 派生一个“Socket”类。这包含 IP/端口、套接字句柄和其他任何可能需要的字符串成员。

从 ITC 派生一个“缓冲区”类。这有一个“BoundSocket”成员、缓冲区空间和一个“OVERLAPPED”结构。

与 IO 线程的通信相当容易。由于它必须等待一些可变的东西,它可以等待一个管理“命令”并发队列的信号量。

如果您的 UI 希望指示 IO 线程连接到服务器,它会创建一个 Socket 实例(新),从 UI 元素加载 IP 和 Port 成员,将 Command 枚举设置为“Connect”,将套接字推送到命令队列并发出信号量 (ReleaseSemaphore)。

IO 线程中的警报等待然后返回 WAIT_OBJECT_0,(它需要忽略返回 WAIT_IO_COMPLETION),因此知道命令已排队。它从命令队列中弹出它并作用于命令枚举(可能打开它),以执行所需的操作。对于连接,这将涉及一个重叠的“ConnectEx”调用来排队连接请求并设置连接完成处理程序。

连接完成处理程序在调用时会检查是否成功连接,如果是,则可以新建一个缓冲区,加载它,发出一个 WSARecv 以供服务器发送内容并将返回的 Socket 对象存储在容器中.如果失败,它可以加载 Socket 带有合适的错误消息并将其 PostMessage 发送回 UI 线程以通知用户失败。

看——没那么难,不需要10000行代码:)

唯一我不知道如何立即做的事情是从完成例程中返回的 OVERLAPPED 结构中获取套接字对象的“this”。在 32 位系统上,我将 Buffer 'this' 推入 Buffer 实例中重叠结构的 hEvent 字段中,并将其转换回完成例程中。 Buffer 实例有一个 Socket 引用,所以工作完成了。在 64 位系统上,hEvent 没有足够的空间来存储 48/64 位“this”缓冲区指针,并且(显然)这需要一个扩展的 OVERLAPPED 结构:( 不确定这是如何完成的 - 也许你会发现:)

[edit] @BenVoigt 对 32/64 位“在完成例程中获取 Socket 上下文 'this'”问题提出了建议 - 这比我想象的要容易:): https://stackoverflow.com/a/28660537/758133

【讨论】:

  • 我应该输入标签C 而不是C++,我不会创建任何类:-)。因此 IO 线程(将执行 WSASend()WSARecv()? 的线程)将自己置于等待状态,但这种等待状态有两件事可以“唤醒它”:1)当完成例程被调用时执行完毕。 2) UI 线程发信号通知它醒来做一些 IO 事情。我在这一点上是对的吗?
  • @John 当然,至少两个。也许如果你想超时,所以它可能有一个非无限超时并在 WAIT_TIMEOUT 唤醒。
猜你喜欢
  • 2016-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多