开发者

In Win32, is there a way to test if a socket is non-blocking?

In Win32, is there a way to test if a socket is non-blocking?

Under POSIX sys开发者_如何学Ctems, I'd do something like the following:

int is_non_blocking(int sock_fd) {
    flags = fcntl(sock_fd, F_GETFL, 0);
    return flags & O_NONBLOCK;
}

However, Windows sockets don't support fcntl(). The non-blocking mode is set using ioctl with FIONBIO, but there doesn't appear to be a way to get the current non-blocking mode using ioctl.

Is there some other call on Windows that I can use to determine if the socket is currently in non-blocking mode?


A slightly longer answer would be: No, but you will usually know whether or not it is, because it is relatively well-defined.

All sockets are blocking unless you explicitly ioctlsocket() them with FIONBIO or hand them to either WSAAsyncSelect or WSAEventSelect. The latter two functions "secretly" change the socket to non-blocking.

Since you know whether you have called one of those 3 functions, even though you cannot query the status, it is still known. The obvious exception is if that socket comes from some 3rd party library of which you don't know what exactly it has been doing to the socket.

Sidenote: Funnily, a socket can be blocking and overlapped at the same time, which does not immediately seem intuitive, but it kind of makes sense because they come from opposite paradigms (readiness vs completion).


Previously, you could call WSAIsBlocking to determine this. If you are managing legacy code, this may still be an option.

Otherwise, you could write a simple abstraction layer over the socket API. Since all sockets are blocking by default, you could maintain an internal flag and force all socket ops through your API so you always know the state.

Here is a cross-platform snippet to set/get the blocking mode, although it doesn't do exactly what you want:

/// @author Stephen Dunn
/// @date 10/12/15
bool set_blocking_mode(const int &socket, bool is_blocking)
{
    bool ret = true;

#ifdef WIN32
    /// @note windows sockets are created in blocking mode by default
    // currently on windows, there is no easy way to obtain the socket's current blocking mode since WSAIsBlocking was deprecated
    u_long flags = is_blocking ? 0 : 1;
    ret = NO_ERROR == ioctlsocket(socket, FIONBIO, &flags);
#else
    const int flags = fcntl(socket, F_GETFL, 0);
    if ((flags & O_NONBLOCK) && !is_blocking) { info("set_blocking_mode(): socket was already in non-blocking mode"); return ret; }
    if (!(flags & O_NONBLOCK) && is_blocking) { info("set_blocking_mode(): socket was already in blocking mode"); return ret; }
    ret = 0 == fcntl(socket, F_SETFL, is_blocking ? flags ^ O_NONBLOCK : flags | O_NONBLOCK);
#endif

    return ret;
}


I agree with the accepted answer, there is no official way to determine the blocking state of a socket on Windows. In case you get a socket from a third party (let's say, you are a TLS library and you get the socket from upper layer) you cannot decide if it is in blocking state or not.

Despite this I have a working, unofficial and limited solution for the problem which works for me for a long time.

I attempt to read 0 bytes from the socket. In case it is a blocking socket it will return 0, in case it is a non-blocking it will return -1 and GetLastError equals WSAEWOULDBLOCK.

int IsBlocking(SOCKET s)
{
    int r = 0;
    unsigned char b[1];
    r = recv(s, b, 0, 0);
    if (r == 0)
        return 1;
    else if (r == -1 && GetLastError() == WSAEWOULDBLOCK)
        return 0;
    return -1; /* In  case it is a connection socket (TCP) and it is not in connected state you will get here 10060 */
}

Caveats:

  • Works with UDP sockets
  • Works with connected TCP sockets
  • Doesn't work with unconnected TCP sockets
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜