Home > Back-end >  Winsock C , faster way to see if port is available than using 'connect'?
Winsock C , faster way to see if port is available than using 'connect'?

Time:07-07

What's the fastest way to see if a TCP port on a remote computer is listening or is available?

After I do a socket call, the select function always returns 0, whether or not the remote computer has port 43210 open or closed!

The test remote computer has its firewall turned on, but has port 43210 open (or closed) for this testing.

#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <mstcpip.h>
#pragma comment(lib, "Ws2_32.lib")      // More than one instance overloaded function has C linkage

DWORD Tryselect()
{
    SOCKET ConnectSocket = INVALID_SOCKET;
    INT recvbuflen = 4096;
    INT iResult;
    sockaddr_in clientService{};
    clientService.sin_family = AF_INET;

    // Create the socket (local)
    ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    timeval timeOut = { 0 };
    timeOut.tv_sec = 5;
    timeOut.tv_usec = 0; 
    fd_set fdset; 
    FD_ZERO(&fdset); 
    FD_SET(ConnectSocket, &fdset); 
    int iRet = select(0, NULL, &fdset, NULL, &timeOut);
    DWORD dwErrWSA = WSAGetLastError();    

    ULONG nInterfaceAddr = 0;
    inet_pton(AF_INET, "192.168.1.12", &nInterfaceAddr);
    clientService.sin_addr.s_addr = nInterfaceAddr;
    clientService.sin_port = htons(43210);
    iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService));

    return 0;
}


int __cdecl main()
{
    Tryselect();
}

I also tried WSAConnectByName, but I could not get the timeout to even work correctly.

CodePudding user response:

What's the fastest way to see if a TCP port on a remote computer is listening or is available?

There is only 1 way to test the availability of a TCP port - attempt to connect() (or equivalent) to the port, and handle any errors that may occur.

After I do a socket call, the select function always returns 0, whether or not the remote computer has port 43210 open or closed!

That is because your test code is wrong, for a number of reasons:

  • You are not initializing the Winsock library, which will cause socket() and select() to fail with a WSANOTINITIALISED error, but you are not handling that condition. On Windows, you MUST call WSAStartup() before calling any other Winsock functions (except for WSAGetLastError()). This is not required on other platforms.

  • You are calling connect() after calling select(), so there is no activity for select() to report status for. You need to swap the order of these 2 functions.

  • You are using the socket in blocking mode, which means connect() will not exit until the operation is finished, whether it is successful or failed. Thus, there is no opportunity to use select(). To use a timeout with connect(), you MUST either:

    • put the socket into non-blocking mode first, then call connect() (or equivalent) and make sure it fails with a WSAEWOULDBLOCK error, and then use select() (or equivalent) to wait for the connect operation to finish or timeout.

    • call connect() in a separate thread than your timeout wait. If the wait times out, you can closesocket() the socket to abort the connect(). On other platforms, close() alone is not guaranteed to abort a blocking connect(), so you would need to shutdown() the socket first.

  • You are using select() to detect only if the connect() operation succeeded, but not detect if it failed. If it did fail, your code would never know that, and keep waiting until the timeout elapsed. On Windows, you need to use select()'s writefds parameter to handle the success case, and the exceptfds parameter to handle the failure case. This is different than other platforms, which only use the writefds parameter for both cases, and then you would use getsockopt(SO_ERROR) to differentiate success from failure.

  • You are leaking the SOCKET. You need to call closesocket() when you are done using it.

With that said, try something more like this instead:

#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <mstcpip.h>

#pragma comment(lib, "Ws2_32.lib")

int Tryselect()
{
    int iErr = 0;

    SOCKET ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ConnectSocket == INVALID_SOCKET)
    {
        iErr = WSAGetLastError();
    }
    else
    {
        u_long mode = 1;
        int iResult = ioctlsocket(ConnectSocket, FIONBIO, &mode);
        if (iResult == SOCKET_ERROR)
        {
            iErr = WSAGetLastError();
        }
        else
        {
            sockaddr_in clientService{};
            clientService.sin_family = AF_INET;
            inet_pton(AF_INET, "192.168.1.12", &clientService.sin_addr);
            clientService.sin_port = htons(43210);

            iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService));
            if (iResult == SOCKET_ERROR)
            {
                iErr = WSAGetLastError();
                if (iErr == WSAEWOULDBLOCK)
                {
                    timeval timeOut = { 0 };
                    timeOut.tv_sec = 5;
                    timeOut.tv_usec = 0; 

                    fd_set wset, eset; 
                    FD_ZERO(&wset); FD_SET(ConnectSocket, &wset); 
                    FD_ZERO(&eset); FD_SET(ConnectSocket, &eset); 

                    iResult = select(0, NULL, &wset, &eset, &timeOut);

                    if (iResult == SOCKET_ERROR)
                    {
                        iErr = WSAGetLastError();
                    }
                    else if (iResult == 0)
                    {
                        iErr = WSAETIMEDOUT;
                    }
                    else
                    {
                        iErr = 0;
                        if (FD_ISSET(ConnectSocket, &eset))
                            getsockopt(ConnectSocket, SOL_SOCKET, SO_ERROR, &iErr);
                    }
                }
            }
        }

        closesocket(ConnectSocket);
    }

    return iErr;
}

int main()
{
    WSADATA wsa;
    int err = WSAStartup(MAKEWORD(2,0), &wsa);
    if (err != 0) ...
    
    err = Tryselect();
    ...
    
    WSACleanup();
    return 0;
}
  • Related