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, theselect
function always returns0
, 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()
andselect()
to fail with aWSANOTINITIALISED
error, but you are not handling that condition. On Windows, you MUST callWSAStartup()
before calling any other Winsock functions (except forWSAGetLastError()
). This is not required on other platforms.You are calling
connect()
after callingselect()
, so there is no activity forselect()
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 useselect()
. To use a timeout withconnect()
, you MUST either:put the socket into non-blocking mode first, then call
connect()
(or equivalent) and make sure it fails with aWSAEWOULDBLOCK
error, and then useselect()
(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 canclosesocket()
the socket to abort theconnect()
. On other platforms,close()
alone is not guaranteed to abort a blockingconnect()
, so you would need toshutdown()
the socket first.
You are using
select()
to detect only if theconnect()
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 useselect()
'swritefds
parameter to handle the success case, and theexceptfds
parameter to handle the failure case. This is different than other platforms, which only use thewritefds
parameter for both cases, and then you would usegetsockopt(SO_ERROR)
to differentiate success from failure.You are leaking the
SOCKET
. You need to callclosesocket()
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;
}