Winsock C++,查看端口是否可用的方法比使用"连接"更快?



查看远程计算机上的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;
}

最新更新