Reputation:
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?
Upvotes: 1
Views: 4174
Reputation: 185
It's possible to use select
, accept
and recv
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 call select
, passing 2 sockets into the readfds
parameter: the listening socket and SocketPairReceive.
For TCP clients, you would also call select
, passing 2 sockets into the readfds
parameter: the client socket and SocketPairReceive.
In my test code, I have the accept
thread, threads for new clients received from accept
, and additional clients connected to the server via connect
, all using the select
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 your accept
and client threads to pass into select
. 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 that select
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 aborting accept
. It's also a little dodgy for the same reasons closesocket
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 for recv
as well, so I ultimately ditched this.WSARecv
and WaitForMultipleObjects
. 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
and recv
with blocking sockets and threads. Hope this helps!
Upvotes: 0
Reputation: 42343
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.
Upvotes: 1
Reputation: 2731
To abort the blocking call to recv()
, you can close the socket with closesocket()
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).
Upvotes: 1