There is a thread likes this:
{
......
while (1)
{
recv(socket, buffer, sizeof(buffer), 0);
......
}
close(socket
I would likely use signals as in @alk's fine answer (also discussed here).
Alternatively, you may use multiplexed I/O.
At initialization, create a global pipe(2). When it is time to end the program, close the write-end of the pipe — now the read-end will instantly select(2)/poll(2) readable (for EOF). Meanwhile, have your recv-blocked threads include the read-end of this pipe along with their sockets in, for example, an indefinitely blocking select(2) call. If the read-end of the pipe returns readable, the I/O threads know it is time to terminate gracefully.
The major caveat of this technique is to ensure that the write-end is closed only once, as a subsequent, naïve close(2) might nip some innocent file that happens to have been given the same descriptor as the pipe's old write-end.