查看远程计算机上的TCP端口是否正在侦听或可用的最快方法是什么?
在我调用socket
之后,无论远程计算机的端口43210是否打开或关闭,select
函数总是返回0
!
测试远程计算机的防火墙已打开,但此测试的端口43210已打开(或关闭)。
#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();
}
我也尝试了WSAConnectByName
,但我甚至无法使超时正常工作。
查看远程计算机上的TCP端口是否正在侦听或可用的最快方法是什么?
只有一种方法可以测试TCP端口的可用性-尝试将connect()
(或等效端口)连接到该端口,并处理可能发生的任何错误。
执行
socket
调用后,无论远程计算机的端口43210是否打开或关闭,select
函数始终返回0
!
这是因为您的测试代码是错误的,原因有很多:
-
您没有初始化Winsock库,这将导致
socket()
和select()
失败并出现WSANOTINITIALISED
错误,但您没有处理这种情况。在Windows上,在调用任何其他Winsock函数(WSAGetLastError()
除外)之前,必须先调用WSAStartup()
。这在其他平台上是不需要的。 -
在调用
select()
之后,您正在调用connect()
,因此select()
没有可报告其状态的活动。您需要交换这两个函数的顺序。 -
您正在阻塞模式中使用套接字,这意味着
connect()
在操作完成之前不会退出,无论操作成功还是失败。因此,没有机会使用select()
。要使用connect()
超时,您必须:-
首先将套接字置于非阻塞模式,然后调用
connect()
(或等效)并确保其失败并出现WSAEWOULDBLOCK
错误,然后使用select()
(或等效设备)等待连接操作完成或超时。 -
在一个单独的线程中调用
connect()
,而不是您的超时等待。如果等待超时,可以closesocket()
套接字中止connect()
。在其他平台上,单独的close()
不能保证中止阻塞的connect()
,因此您需要首先shutdown()
套接字。
-
-
您使用
select()
仅检测connect()
操作是否成功,而不检测操作是否失败。如果它确实失败了,您的代码将永远不会知道这一点,并一直等待,直到超时为止。在Windows上,需要使用select()
的writefds
参数来处理成功案例,使用exceptfds
参数来处理失败案例。这与其他平台不同,后者在这两种情况下都只使用writefds
参数,然后使用getsockopt(SO_ERROR)
来区分成功与失败。 -
您正在泄漏
SOCKET
。当你使用完它时,你需要调用closesocket()
。
话虽如此,还是试试类似的东西:
#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))
{
int size = sizeof(iErr);
getsockopt(ConnectSocket, SOL_SOCKET, SO_ERROR, &iErr, &size);
}
}
}
}
}
closesocket(ConnectSocket);
}
return iErr;
}
int main()
{
WSADATA wsa;
int err = WSAStartup(MAKEWORD(2,0), &wsa);
if (err != 0) ...
err = Tryselect();
...
WSACleanup();
return 0;
}