voteforpedro
voteforpedro

Reputation:

How to abort winsock blocking call?

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

Answers (3)

TByte
TByte

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.

  1. 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.

  2. For your TCP server, the accept thread would call select, passing 2 sockets into the readfds parameter: the listening socket and SocketPairReceive.

  3. 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:

  • Using 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.
  • Using 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.
  • You may abort an 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.
  • On Windows, you can use 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

Warren Young
Warren Young

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

Christophe Vu-Brugier
Christophe Vu-Brugier

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

Related Questions