如何防止WSASend()阻止我的UI线程?

时间:2022-09-22 20:55:54

I want to use Overlapped I/O with Completion Routine to handle client connections.

我想使用Overlapped I / O和Completion Routine来处理客户端连接。

In my UI thread I want to use WSASend(), but in order for the system to call my callback function to inform me that data has been sent, the UI thread must be in a wait state, but this will freeze my UI!

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

How should I fix this problem?

我该如何解决这个问题?

1 个解决方案

#1


1  

I agree with @DavidHeffernan - the UI thread should be doing UI things. The IO thread surely needs a binding and port, (server), or peer address and port(client). The socket from ConnectEx or AcceptEx is surely better loaded in the IO thread, but a Socket class with, (at this time, undefined), socket member could surely be created in the UI thread and signaled into the IO thread for handling. Whether buffers form part of your Socket class, or a separate Buffer class, is a design consideration.

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

One implementation, (that I have used successfully):

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

Design/define an 'Inter Thread Comms', (ITC'), message class. This has a 'command' enum member that can tell other threads to do stuff, together with any other useful stuff that might be required in such a message

设计/定义'Inter Thread Comms',(ITC'),消息类。这有一个'command'枚举成员,它可以告诉其他线程做什么,以及这样的消息中可能需要的任何其他有用的东西

Derive a 'Socket' class from ITC. This has string members for the IP/port, the socket handle and anything else that may be required.

从ITC获得'Socket'类。它具有IP /端口的字符串成员,套接字句柄以及可能需要的任何其他内容。

Derive a 'Buffer' class from ITC. This has a 'BoundSocket' member, buffer-space and an 'OVERLAPPED' struct.

从ITC获得“缓冲”类。它有一个'BoundSocket'成员,缓冲区空间和'OVERLAPPED'结构。

Comms with the IO thread is fairly easy. Since it has to wait on something altertably, it can wait on a semaphore that manages a 'Commands' ConcurrentQueue.

使用IO线程的通信相当容易。由于它必须等待某些东西,它可以等待管理“命令”ConcurrentQueue的信号量。

If you UI wishes to instruct the IO thread to, say, connect to a server, it creates a Socket instance, (new), loads the IP and Port members from UI elements, sets the Command enum to 'Connect', pushes the socket onto the Commands queue and signals the semaphore, (ReleaseSemaphore).

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

The alertable wait in the IO thread then returns with WAIT_OBJECT_0, (it needs to ignore returns with WAIT_IO_COMPLETION) and so knows that a command has ben queued. It pops it from the Commands queue and acts upon the command enum, (maybe switching on it), to perform the required action/s. For connect, this would involve an overlapped 'ConnectEx' call to queue up a connect request and set up the connect completion handler.

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

The connect completion handler, when called, checks for a succesfull connect and, if so, could new up a Buffer, load it, issue a WSARecv with it for the server to send stuff and store the returned Socket object in a container. If failed, it could load the Socket with a suitable error message and PostMessage it back to the UI thread to inform the user of the fail.

连接完成处理程序在被调用时会检查成功连接,如果是这样,可以新建一个Buffer,加载它,发出一个WSARecv,服务器发送东西并将返回的Socket对象存储在容器中。如果失败,它可以使用适当的错误消息加载Socket并将其PostMessage返回到UI线程以通知用户失败。

See - it's not that difficult and does not need 10000 lines of code:)

看 - 它并不困难,不需要10000行代码:)

The only thing I don't know how to do immediately is getting the 'this' for the socket object back from the OVERLAPPED struct that is returned in the completion routine. On 32-bit systems, I shoved the Buffer 'this' into the hEvent field of the overlapped struct in the Buffer instance and cast it back in the completion routine. The Buffer instance has a Socket reference, so the job was done. On 64-bit systems, hEvent has not enough room to store the 48/64-bit 'this' Buffer pointer and, (aparrently), this required an extended OVERLAPPED struct:( Not sure how that is done - maybe you will find out:)

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

[edit] @BenVoigt has advice on the 32/64 bit 'getting the Socket context 'this' back in the completion routine' issue - it's easier than I thought:): https://*.com/a/28660537/758133

[编辑] @BenVoigt建议32/64位'获取Socket上下文'这个'回到完成例程'问题 - 这比我想象的要容易:):https://*.com/a/28660537/758133

#1


1  

I agree with @DavidHeffernan - the UI thread should be doing UI things. The IO thread surely needs a binding and port, (server), or peer address and port(client). The socket from ConnectEx or AcceptEx is surely better loaded in the IO thread, but a Socket class with, (at this time, undefined), socket member could surely be created in the UI thread and signaled into the IO thread for handling. Whether buffers form part of your Socket class, or a separate Buffer class, is a design consideration.

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

One implementation, (that I have used successfully):

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

Design/define an 'Inter Thread Comms', (ITC'), message class. This has a 'command' enum member that can tell other threads to do stuff, together with any other useful stuff that might be required in such a message

设计/定义'Inter Thread Comms',(ITC'),消息类。这有一个'command'枚举成员,它可以告诉其他线程做什么,以及这样的消息中可能需要的任何其他有用的东西

Derive a 'Socket' class from ITC. This has string members for the IP/port, the socket handle and anything else that may be required.

从ITC获得'Socket'类。它具有IP /端口的字符串成员,套接字句柄以及可能需要的任何其他内容。

Derive a 'Buffer' class from ITC. This has a 'BoundSocket' member, buffer-space and an 'OVERLAPPED' struct.

从ITC获得“缓冲”类。它有一个'BoundSocket'成员,缓冲区空间和'OVERLAPPED'结构。

Comms with the IO thread is fairly easy. Since it has to wait on something altertably, it can wait on a semaphore that manages a 'Commands' ConcurrentQueue.

使用IO线程的通信相当容易。由于它必须等待某些东西,它可以等待管理“命令”ConcurrentQueue的信号量。

If you UI wishes to instruct the IO thread to, say, connect to a server, it creates a Socket instance, (new), loads the IP and Port members from UI elements, sets the Command enum to 'Connect', pushes the socket onto the Commands queue and signals the semaphore, (ReleaseSemaphore).

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

The alertable wait in the IO thread then returns with WAIT_OBJECT_0, (it needs to ignore returns with WAIT_IO_COMPLETION) and so knows that a command has ben queued. It pops it from the Commands queue and acts upon the command enum, (maybe switching on it), to perform the required action/s. For connect, this would involve an overlapped 'ConnectEx' call to queue up a connect request and set up the connect completion handler.

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

The connect completion handler, when called, checks for a succesfull connect and, if so, could new up a Buffer, load it, issue a WSARecv with it for the server to send stuff and store the returned Socket object in a container. If failed, it could load the Socket with a suitable error message and PostMessage it back to the UI thread to inform the user of the fail.

连接完成处理程序在被调用时会检查成功连接,如果是这样,可以新建一个Buffer,加载它,发出一个WSARecv,服务器发送东西并将返回的Socket对象存储在容器中。如果失败,它可以使用适当的错误消息加载Socket并将其PostMessage返回到UI线程以通知用户失败。

See - it's not that difficult and does not need 10000 lines of code:)

看 - 它并不困难,不需要10000行代码:)

The only thing I don't know how to do immediately is getting the 'this' for the socket object back from the OVERLAPPED struct that is returned in the completion routine. On 32-bit systems, I shoved the Buffer 'this' into the hEvent field of the overlapped struct in the Buffer instance and cast it back in the completion routine. The Buffer instance has a Socket reference, so the job was done. On 64-bit systems, hEvent has not enough room to store the 48/64-bit 'this' Buffer pointer and, (aparrently), this required an extended OVERLAPPED struct:( Not sure how that is done - maybe you will find out:)

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

[edit] @BenVoigt has advice on the 32/64 bit 'getting the Socket context 'this' back in the completion routine' issue - it's easier than I thought:): https://*.com/a/28660537/758133

[编辑] @BenVoigt建议32/64位'获取Socket上下文'这个'回到完成例程'问题 - 这比我想象的要容易:):https://*.com/a/28660537/758133