Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Workaround to fix connecting nonblocking tcp sockets on Windows  (Read 5876 times)

0 Members and 1 Guest are viewing this topic.

Powereleven

  • Newbie
  • *
  • Posts: 34
    • View Profile
Workaround to fix connecting nonblocking tcp sockets on Windows
« on: December 20, 2020, 11:28:48 pm »
Hi,
today I decided to try nonblocking tcp socket so I could have a UI that is able to cancel the connecting operation more realiably than using a small timeout in the blocking socket in the connect function, but I found out that calling connect and checking for Done doesn't work on Windows. I found 2 threads from 2012 that mention this problem. https://en.sfml-dev.org/forums/index.php?topic=7435.0 and https://github.com/SFML/SFML/issues/194
Were they ever fixed? Anyway, I'm no expert on windows winsock, so I empirically tested and found out that sleep() a bit after calling connect was enough to work (by work I mean, trying to send bytes worked, even though connect had returned status NotReady), so I suspect that when you call connect, the internal socket is wrongly rebuilt, so it's as if you were always restarting the operation, and it never gets done, as it takes a few milliseconds to connect. Anyway... I coded a small workaround that seems to work fine. Here's some pseudocode. Instead of the more documentation compliant code:
mySocket.setBlocking(false);
while (mySocket.connect(HOST, PORT) != sf::Socket::Status::Done);
mySocket.setBlocking(true); // I only use nonblocking during the first connection phase
 
I do
mySocket.setBlocking(false);
mySocket.connect(HOST, PORT);
while (!mySocket.isConnected())
{
    if (IWantToCancelConnectAttempt)
    {
        mySocket.disconnect();
        goto wherever...
    }
}
mySocket.setBlocking(true);
...
 
where isConnected is implemented like this, which is basically a small portion of the main connect method that doesn't rebuild the socket, but rather just checks what I need
bool TcpSocket::isConnected()
{
        fd_set selector;
        FD_ZERO(&selector);
        FD_SET(getHandle(), &selector);
        timeval time{};
        return select(static_cast<int>(getHandle() + 1), NULL, &selector, NULL, &time);
}
 
I find this documentation confusing https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-select, but apparently timeval == NULL is used for blocking operations, but using it with 0s returns immediately.
That's it. I hope that's useful for anyone having this problem. By the way, where is the most appropriate place to post these kinds of things?
« Last Edit: December 20, 2020, 11:47:35 pm by Powereleven »