如何中止winsock阻塞调用?
我在 C++ 中使用 Winsock 2,并且想知道如何使我的服务器停止从客户端连接读取数据。读取线程在 recv()
中被阻塞,我不知道如何中止它。实现此目的的一种方法是使用带有 select()
的非阻塞套接字,但该线程必须在检查新的 select()
之前等待。
停止读取套接字的正确方法是什么?
I use Winsock 2 in C++, and wonder how to make my server stop reading from the client connection. The reading thread gets blocked in recv()
and I have no idea how to abort it. One way to do this is use non-blocking sockets with select()
, but this thread has to wait before checking the new select()
.
What is the proper way to stop reading the socket?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
要中止对
recv()
的阻塞调用,您可以从另一个线程中使用closesocket()
关闭套接字。即使它有点难看,它也应该有效。您也可以尝试
shutdown()
套接字(我从未测试过)。To abort the blocking call to
recv()
, you can close the socket withclosesocket()
from another thread. Even if it's a bit ugly, it should work.You can try to
shutdown()
the socket too (I never tested that).如果你的程序除了使用套接字 I/O 之外还有其他事情要做,那么你就不应该首先阻塞。
您声称您的线程必须等待,但这只是程序当前设计的反映。您应该重新设计它,以便它可以与某种形式的非阻塞套接字一起使用。每个网络问题都可以使用非阻塞套接字来解决。
由于您使用的是 Winsock,因此这里有许多替代方案,而不仅仅是<代码>选择()。仅当您的应用程序必须在许多平台上运行并且您无法使用每个现代平台上可用的高级(但相互不兼容)替代方案时,
select()
才是一个不错的选择。If your program has other things to do besides working with socket I/O, you should not block in the first place.
You claim your thread must wait, but that's just a reflection of your program's current design. You should redesign it so it works with some form of non-blocking sockets. Every networking problem can be addressed using non-blocking sockets.
Since you're using Winsock, you have many alternatives here, not just
select()
.select()
is a good choice only if your application must run on many platforms and you are unable to use the superior (but mutually incompatible) alternatives available on every modern platform.可以使用
select
、accept
和recv
而无需轮询或超时间隔。无论您想要一个服务器还是只是一个客户端(或多个客户端),该解决方案都可以正常工作。这个答案假设您将有一个专用线程用于调用
accept
,并且每个连接的客户端都有一个线程。创建一个套接字对(在 Windows 上,您需要创建一个本地主机服务器并连接一个客户端,然后关闭监听套接字)。调用两个套接字 SocketPairSend 和 SocketPairReceive。
对于您的 TCP 服务器,
accept
线程将调用select
,将 2 个套接字传递到readfds
参数:侦听套接字和 SocketPairReceive。对于 TCP 客户端,您还可以调用
select
,将 2 个套接字传递到readfds
参数:客户端套接字和 SocketPairReceive。在我的测试代码中,我有
accept
线程、从accept
接收到的新客户端的线程,以及通过connect
连接到服务器的其他客户端,全部使用select
调用,传递相同的额外 SocketPairReceive 套接字。当您想要关闭服务器时,只需在 SocketPairSend 上发送一个字节,所有线程都会从其
select
调用中唤醒。如果您已经有服务器,则可以避免使用套接字对。您只需连接一个虚拟客户端并使用其套接字(从
accept
获得)即可将您的accept
和客户端线程传递给select
。但我更喜欢套接字对方法,因为它适用于所有场景(即,当您没有 TCP 服务器时)。 Socketpair 更好地支撑了这个概念 - 它充当一个您可以等待的事件,一个select
可以接受的事件(Linux 在这方面更灵活,但在 Windows 上,select
只能接受套接字)。关于我遇到的其他建议的一句话:
closesocket
似乎可以工作,但在其文档中明确指出,在另一个阻塞调用正在进行时不要这样做。它看起来也很老套。还有一个需要注意的赛车条件。shutdown
仅在某些情况下有效。它永远不会用于中止accept
。由于与closesocket
相同的原因,它也有点狡猾。从插座系统脚下清扫地毯可能会产生奇怪的结果。accept
- 简单且无害,但有点hacky。我需要一个也适用于recv
的解决方案,所以我最终放弃了这个。WSARecv
和WaitForMultipleObjects
。我没试过。我在这篇旧的 Google 网上论坛帖子此处。在每个人都跳出来说我不应该使用阻塞套接字等之前,我想要一个简单的解决方案,对于我正在开发的一个简单项目,并感到有必要使用旧的
select 编写一个对我有用的答案
、accept
和recv
具有阻塞套接字和线程。希望这有帮助!It's possible to use
select
,accept
andrecv
without polling or timeout intervals.Whether you want a server or just a client (or many clients), this solution will work all the same. This answer assumes you will have a dedicated thread for calling
accept
, and a thread per each client that connects in.Create a socketpair (on Windows, you would need to create a localhost server and connect a client to it, then close the listening socket). Call the two sockets SocketPairSend and SocketPairReceive.
For your TCP server, the
accept
thread would callselect
, passing 2 sockets into thereadfds
parameter: the listening socket and SocketPairReceive.For TCP clients, you would also call
select
, passing 2 sockets into thereadfds
parameter: the client socket and SocketPairReceive.In my test code, I have the
accept
thread, threads for new clients received fromaccept
, and additional clients connected to the server viaconnect
, all using theselect
call, passing the same extra SocketPairReceive socket.When you want to shut the server down, simply send a byte on SocketPairSend, and all the threads would wake up from their
select
calls.You could avoid using a socketpair if you already have a server. You can just connect a dummy client in and use its socket (that you got from
accept
) for youraccept
and client threads to pass intoselect
. But I prefer the socketpair approach because it works for all scenarios (i.e., when you don't have a TCP server). Socketpair underpins the concept better - it acts as an event that you can wait on, one thatselect
can accept (Linux is more flexible in this regard, but on Windows,select
can only accept sockets).A word on other suggestions that I've come across:
closesocket
appears to work but it's explicitly stated in its documentation to not do this while another blocking call is in progress. It also seems hacky. There's also a racing condition to be aware of.shutdown
only works in some scenarios. It never works for abortingaccept
. It's also a little dodgy for the same reasonsclosesocket
is. Sweeping the rug from under the socket system's feet may potentially create weird results.accept
by connecting a client in - simple and harmless, but a little hacky. I needed a solution that worked forrecv
as well, so I ultimately ditched this.WSARecv
andWaitForMultipleObjects
. I've not tried it. I saw it on this old Google Groups post here.Before everyone jumps to say that I should not be using blocking sockets etc, well I wanted a simple solution, for a simple project I am working on and felt compelled to write an answer that worked for me, using the good old
select
,accept
andrecv
with blocking sockets and threads. Hope this helps!