Winsock2: How to unblock from a blocking 'connect' call?

I'm developing a C++ app that uses a company "Socket" library (call it XSocket). XSocket uses Winsock2 underneath the hood and the API only allows use of a blocking socket. I don't want to discuss modifying XSocket; there's no technical reason why it can't be done, red tape prevents me from doing so.

Rather, I would like to discuss how to work with blocking sockets. The app, in Model-View-Controller terms, consists of a GUI thread, and a model/controller thread. I have the model thread spawn a child thread to manage blocking socket communication with an external system (so the model thread can continue to do other work).

With regards to the child thread, one immediate issue is how to force execution to return from a blocking "connect" call.

Winsock2 documentation [1] mentions that while a blocking socket is connecting, it's put in an alertable wait state that can be interrupted using an Asynchronous Procedure Call (APC). I've successfully used APC to interrupt a blocking recv call and can probably use it again to interrupt connect. However, I was wondering if using APCs is the preferred technique since it seems hacky. I guess the only other option is to have the model (parent) thread terminate the child thread but this seems like a nuclear option.

APC doesn't seem to be a portable solution. How do folks use blocking sockets in Linux using BSD sockets? Do they typically spawn a thread to handle blocking calls? How do they interrupt that call?

Edited #1: This question has been answered here. There appears to be a race condition resulting in a pending/queued APC that wasn't triggered to exit the connect call.

I believe I also read somewhere that the BSD-sockets approach in linux is to use select().

Lots of related articles here [3]. Specifically on I/O strategy selection [4].

[1] MSDN: winsock2
https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect

[2] How to exit a blocking connect() call in Windows?
https://stackoverflow.com/questions/42440230/how-to-exit-a-blocking-connect-call-in-windows

[3] Winsock Programmer's FAQ
https://tangentsoft.com/wskfaq/

[4] Which I/O Strategy Should I Use
https://tangentsoft.com/wskfaq/articles/io-strategies.html
Last edited on
If you create a blocking socket, which is the default, then well... operations like connect(), send() and recv() are going to block the calling thread. If you want to avoid the "main" thread to get blocked, you could move the call to a separate "worker" thread, but then there still is no clean way to "abort" the operation from another thread. What you can do is setting up a time-out for these operations, so that the calling thread won't be blocked forever. Specifically, you may want to set SO_RCVTIMEO and SO_SNDTIMEO via the setsockopt() function.

According to the manpage:
https://man7.org/linux/man-pages/man7/socket.7.html

Specify the receiving or sending timeouts until reporting an error. The argument is a struct timeval. If an input or output function blocks for this period of time, and data has been sent or received, the return value of that function will be the amount of data transferred; if no data has been transferred and the timeout has been reached, then -1 is returned with errno set to EAGAIN or EWOULDBLOCK, or EINPROGRESS (for connect(2)) just as if the socket was specified to be nonblocking. If the timeout is set to zero (the default), then the operation will never timeout. Timeouts only have effect for system calls that perform socket I/O (e.g., accept(2), connect(2), read(2), recvmsg(2), send(2), sendmsg(2)) [...]



Your other option is to use non-blocking sockets, e.g., by passing the SOCK_NONBLOCK flag to socket() when creating the socket, or by using the fcntl() function with the O_NONBLOCK flag to switch an existing socket into non-blocking mode. This way, operations like connect(), send() and recv() will not block at all, but rather return immediately with an error code like EAGAIN or EWOULDBLOCK (or EINPROGRESS).

Instead of repeatedly re-trying the operation on the non-blocking socket in a loop (which is very inefficient), you would then use the poll() (or epoll) mechanism to figure out when a socket becomes ready for reading and/or writing:
https://man7.org/linux/man-pages/man2/poll.2.html

Note that poll() allows you to "monitor" multiple sockets at the same time, so you can react as soon as any of them becomes ready. Also, the poll() function has an explicit time-out parameter, so you can be sure it will return after this amount of time.
Last edited on
Registered users can post here. Sign in or register to post.