接上一章节,在明确了具体客户机是活着,就可以通过对个IP 地址的特定端口发起连接,如果可以连接就可以判断这个端口是开放的,很多时候会使用Telnet 和连接某个IP 的端口原理相同,但是注意从程序角度来讲TCP 和UDP 是有区别的,你用Telnet 去连接一个UDP 的端口一样是失败,因为UDP 也不需要连接,今天还是先讨论如何探测一个IP 上一个范围内的TCP端口是否是开放的。
所谓的探测其实就是从你的电脑对它的电脑基于TCP 发起一次连接,可以连接上(握手)就说明它这个端口至少是开放的,至于如何接下来跟这个端口进行合法或非合法的通讯就不是今天讨论的话题。
直接上代码:
这里注意一点作为探测用途,而且我们这里还是一个大队列的连接,连接上后返回应该立刻断开关闭连接。不要只连接不断开,无论是什么系统能同时维持的连接数、句柄数、线程数是有总限制的,一旦堵塞死了就真死机了。TCP端口的可取范围从 1 – 65535 之间,一般不需要从头扫于尾,会非常耗费时间,而且容易造成资源回收不及时出问题。
建议可以设置一个小范围分段来试探。
因为探测时采用的是阻塞式连接,一定使用多线程,在线程中开一个循环扫描一个端口段,不用多线程必须卡死。
//
//定义线程函数传入参数的结构体
发起探测线程:
//
通过这种方式就可以试探出这台客户机上那些TCP 端口是可以连接的。居然远程探测的效率不高,但是毕竟不需要在目标机器安装客户端程序也有另类的用途。
所谓的探测其实就是从你的电脑对它的电脑基于TCP 发起一次连接,可以连接上(握手)就说明它这个端口至少是开放的,至于如何接下来跟这个端口进行合法或非合法的通讯就不是今天讨论的话题。
直接上代码:
int testTCPConnect( const char* clientIP ,int port )
{
/*
//加载套接字库
WORDwVersionRequested;
WSADatawsaData;
interr;
printf("Thisis a Client side application!\n");
wVersionRequested= MAKEWORD(2, 2);
err= WSAStartup(wVersionRequested, &wsaData);
if(err != 0)
{
//Tellthe user that we could not find a usable
//WinSockDLL.
printf("WSAStartup()called failed!\n");
return-1;
}
else
{
printf("WSAStartup()called successful!\n");
}
if(LOBYTE(wsaData.wVersion) != 2 ||
HIBYTE(wsaData.wVersion)!= 2) {
//Tell the user that we could not find a usable
//WinSock DLL.
WSACleanup();
return-1;
}
*/
/*The WinSock DLL is acceptable. Proceed. */
//创建套接字
SOCKETsockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockClient == INVALID_SOCKET)
{
printf("socket()called failed! ,error code is: %d", WSAGetLastError());
return-1;
}
else
{
printf("socket()called successful!\n");
}
//需要连接的服务端套接字结构信息
SOCKADDR_INaddrServer;
addrServer.sin_addr.S_un.S_addr= inet_addr( clientIP );//设定服务器的IP地址
addrServer.sin_family= AF_INET;
addrServer.sin_port= htons( port );//设定服务器的端口号(使用网络字节序)
intiTimeOut = 200;
if( setsockopt(sockClient, SOL_SOCKET, SO_SNDTIMEO, (char *)&iTimeOut,sizeof(iTimeOut)) == SOCKET_ERROR){
return-2;
}
if( setsockopt(sockClient, SOL_SOCKET, SO_RCVTIMEO, (char *)&iTimeOut,sizeof(iTimeOut)) == SOCKET_ERROR){
return-2;
}
//向服务器发出连接请求
interr = connect(sockClient, (SOCKADDR*)&addrServer, sizeof(SOCKADDR));
if(err == SOCKET_ERROR)
{
printf("connect()called failed! The error code is: %d\n", WSAGetLastError());
return-1;
}
else
{
printf("connect()called successful\n");
}
//关闭套接字
closesocket(sockClient);
/*
//终止套接字库的使用
WSACleanup();
*/
return0;
}
这里注意一点作为探测用途,而且我们这里还是一个大队列的连接,连接上后返回应该立刻断开关闭连接。不要只连接不断开,无论是什么系统能同时维持的连接数、句柄数、线程数是有总限制的,一旦堵塞死了就真死机了。TCP端口的可取范围从 1 – 65535 之间,一般不需要从头扫于尾,会非常耗费时间,而且容易造成资源回收不及时出问题。
建议可以设置一个小范围分段来试探。
因为探测时采用的是阻塞式连接,一定使用多线程,在线程中开一个循环扫描一个端口段,不用多线程必须卡死。
//
//定义线程函数传入参数的结构体
typedef struct __THREAD_DATA
{
intsPort = 0;
intePort = 0;
charclientIP[20];
}THREAD_DATA;
HANDLE g_hMutex = NULL; //互斥量
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
THREAD_DATA*pThreadData = (THREAD_DATA*)lpParameter;
for(int i = pThreadData->sPort; i < pThreadData->ePort; i++ )
{
//请求获得一个互斥量锁
WaitForSingleObject(g_hMutex,INFINITE);
//std::cout<< pThreadData->strThreadName << " --- " << i<< std::endl;
intport = i;
intz = testTCPConnect(pThreadData->clientIP, port);
CWnd*pWnd = AfxGetApp()->GetMainWnd();
CRemoteConnectTestDlg*pMain = (CRemoteConnectTestDlg*)pWnd;
CStringokPort;
okPort.Format(L"%d",port);
if( z == 0 )
{
pMain->m_CPortOkList.AddString(okPort + L" :: Open");
pMain->m_CPortOkList.SendMessage(WM_KEYDOWN,VK_DOWN, NULL);
}
else
{
pMain->m_cPortList.AddString(okPort + L" :: Not Open" );
pMain->m_cPortList.SendMessage(WM_KEYDOWN,VK_DOWN, NULL);
}
//Sleep(100);
//释放互斥量锁
ReleaseMutex(g_hMutex);
}
return0L;
}
发起探测线程:
//
//创建一个互斥量
g_hMutex= CreateMutex(NULL, FALSE, NULL);
//初始化线程数据
THREAD_DATA*threadData1 = new THREAD_DATA();
threadData1->sPort= sport_i;
threadData1->ePort= eport_i;
memset(threadData1->clientIP,0, 20);
strcpy_s(threadData1->clientIP,str);
HANDLEhThread1 = CreateThread(NULL, 0, ThreadProc, (LPVOID)threadData1, 0, NULL);
CloseHandle(hThread1);
通过这种方式就可以试探出这台客户机上那些TCP 端口是可以连接的。居然远程探测的效率不高,但是毕竟不需要在目标机器安装客户端程序也有另类的用途。