I have a socket listener which hangs on recv function:
size_t recvLen = recv(sock, buf, 512, 0);
I would like to terminate this thread with interrupting it. MSDN says:
When issuing a blocking Winsock call such as recv, Winsock may need to wait for a network event before the call can complete. Winsock performs an alertable wait in this situation, which can be interrupted by an asynchronous procedure call (APC) scheduled on the same thread.
How can I do that?
You can interrupt it by queuing an APC to it via
QueueUserAPC
. However, it's most likely unsafe to terminate the thread in the APC. Queuing an APC doesn't end therecv
, it just interrupts it; once the APC returns, it will go back to waiting onrecv
again.If you want to stop the
recv
completely, you should be usingselect
with a timeout to wait until data is available. You can then check whether you should keep waiting for data or continue at each timeout.I think the best way to handle this problem is to put the socket into non-blocking I/O mode, so that the thread will never block inside recv() (or in send(), for that matter). The thread should only ever block inside select() (or WaitMultipleObjects()). That way the select() (or WaitMultipleObjects()) call will return if data arrives for the socket (in which case you can then call recv() to get the new data without blocking), but you can also have select()/WaitMultipleObjects() return when something else happens; e.g. when it gets a prompt from the main thread. If you are using select(), that prompt can be the main thread sending a byte on a different socket-pair (with the main thread holding one end of the socket-pair, and the I/O thread holding the other end); if you are using WaitMultipleObjects() then I believe you can use any of the standard Windows event/signaling methods that would cause WaitMultipleObjects() to return.
If you don't want to receive any more data, you can kill the socket at anytime. Just call close() on it, the function in question will immediately return an error.
What I've done in the past is just run another thread with a timeout, after the waiting period if a "don't die" flag isn't set kill the socket.
Errrr... Perform an APC on the same thread? :-))
Seriously, though, it's not clear what your objective here is.
If you just want to end the thread, use the
TerminateThread
function.If you want to interrupt this particular call, you can close the socket.
The only way to really interrupt a blocking
recv()
call and make it fully exit is to close the socket from another thread context than the one that is blocked. If that is not an option, then you need to re-write your socket logic. Best to use non-blocking or asynchronous I/O, that wayrecv()
(orWSARecv()
for the latter) will never block, and you can do whatever you need to do (like checking for thread termination conditions) while the reading is performed in the background.Checking the socket buffer prior to
recv
is more flexible rather than covering a lot forselect()
support, I think. You can callioctlsocket(SockHandle, FIONREAD, Longint(CountInBuffer))
to see if there's data in network buffer to read and then callrecv(SockHandle, buff, CountInBuffer, 0)
. This way you can make a single recv call to read the whole network read buffer if you allocate the buff itself enough with CountInBuffer. Otherwise, you need to call recv in a loop to read the network buffer, which is the traditional way. In both cases, you're still in the constraints of theCountInBuffer
.