1、使用异步Socket
当使用Connect与远程计算机进行连接的时候。如果对方计算机无法连接时,connect函数会等待75秒。这段时间很长,实际中我肯定不想让一个connect函数去等待那么长的时间。同样,再使用send,recv函数的时候,当发送缓冲区满或接受缓冲区满时函数也会阻塞。看看异步socket的特点,可以解决这个connect问题。可以设置socket的特点解决send,recv的超时等待问题。代码如下:
SOCKET Sock;
int iPort = 0;
TCHAR lszIP[16] = {_T('/0')};
char szRet[8] = {0};
WSADATA wsaData;
CString sPort = _T("");
//GetDlgItem(IDC_EDIT1)->GetWindowTextW(lszIP,sizeof(lszIP));
//GetDlgItem(IDC_EDIT2)->GetWindowTextW(sPort);
//iPort = _wtoi(sPort.GetBuffer());
//版本协商
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
// 判断iResult 结果
// 创建Socket
Sock = socket ( AF_INET , SOCK_STREAM , 0 );
if ( Sock < 0 ) {
return ;
}
try {
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(short(iPort));
serveraddr.sin_addr.S_un.S_addr = inet_addr((const char *)UnicodeToAnsi(lszIP,NULL));
//set Recv and Send time out
int TimeOut=6000; //设置发送超时6秒
if (::setsockopt(Sock,SOL_SOCKET,SO_SNDTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR){
return ;
}
TimeOut=6000; //设置接收超时6秒
if (::setsockopt(Sock,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR){
return ;
}
//设置非阻塞方式连接
unsigned long ul = 1;
int ret = ioctlsocket(Sock, FIONBIO, (unsigned long*)&ul);
if (ret == SOCKET_ERROR)
return ;
// 设置非阻塞方式后connect会立即返回,使用select来判断时候连接成功
connect ( Sock , (struct sockaddr*)&serveraddr , sizeof(serveraddr) );
struct timeval timeout ;
fd_set r;
FD_ZERO(&r);
FD_SET(Sock, &r);
timeout.tv_sec = 2; //连接超时2秒
timeout.tv_usec =0;
ret = select(0, 0, &r, 0, &timeout);
if ( ret <= 0 )
{
::closesocket(Sock);
return ;
}
//一般非锁定模式套接比较难控制,可以根据实际情况考虑 再设回阻塞模式
unsigned long ul1= 0 ;
ret = ioctlsocket(Sock, FIONBIO, (unsigned long*)&ul1);
if(ret==SOCKET_ERROR){
::closesocket (Sock);
return ;
}
// 发送信息给服务器程序
{
if ( 1 == send(Sock,"F",1,0) ) {
if ( 1 == recv(Sock,szRet,1,0) ) {
closesocket(Sock);
return ;
}
else {
closesocket(Sock);
return ;
}
}
}
}
catch ( ... ){
TCHAR szError[DEF_TEMP_BUF]={0};
gszGetLastErrorText(szError, DEF_TEMP_BUF);
}
return ;
|
2、使用指定端口与服务器进行连接
再第二篇中的
setWinSock函数中,我没有指定用哪个端口来连接。如果我在firewall上面设置了,只开放一部分端口,那我怎么用这部分端口来与远程计算机进行通讯?代码如下:
struct sockaddr_in stlocalSsin;
stlocalSsin.sin_family = AF_INET;
stlocalSsin.sin_port
= htons(short(9999));
stlocalSsin.sin_addr.S_un.S_addr = inet_addr((const char *)UnicodeToAnsi(lszIP,NULL));
// 设置本地Socket信息
if ( bind(Sock,(struct sockaddr*)&stlocalSsin,sizeof(stlocalSsin)) < 0) {
return ;
}
// connect
|
这样可以看到使用了9999号端口与远程计算机进行连接。
3、判断端口占用情况
察看本地侦听端口可以使用bind函数看看是否成功。也可以使用connect看看是否能够连接上。那netstat命令是怎样获得端口使用信息的呢?用了个反编译工具在netstat中找到了很多函数。最后找到一个GetTcpTable函数:
The GetTcpTable function retrieves the TCP connection table。同样可是使用GetUdpTable获得相关信息。代码如下:
// Declare and initialize variables PMIB_TCPTABLE pTcpTable; pTcpTable = (MIB_TCPTABLE*) malloc(sizeof(MIB_TCPTABLE)); DWORD dwSize = 0; // Make an initial call to GetTcpTable to // get the necessary size into the dwSize variable if (GetTcpTable(pTcpTable, &dwSize, TRUE) == ERROR_INSUFFICIENT_BUFFER) { GlobalFree(pTcpTable); pTcpTable = (MIB_TCPTABLE*) malloc ((UINT) dwSize); } // Make a second call to GetTcpTable to get // the actual data we require if ((dwRetVal = GetTcpTable(pTcpTable, &dwSize, TRUE)) == NO_ERROR) { for (int i = 0; i < (int) pTcpTable->dwNumEntries; i++) { printf("State: %ld/n", pTcpTable->table[i].dwState); } } else { printf("/tCall to GetTcpTable failed./n"); LPVOID lpMsgBuf; if (FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwRetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf, 0, NULL )) { printf("/tError: %s", lpMsgBuf); } LocalFree( lpMsgBuf ); } |
使用完这个函数后可以看
dwState来确定状态。例如:
MIB_TCP_STATE_CLOSED
==
closed
MIB_TCP_STATE_LISTEN
==
Listening
MIB_TCP_STATE_SYN_SENT
==
SYN Sent
MIB_TCP_STATE_SYN_RCVD
==
SYN Received
MIB_TCP_STATE_ESTAB
==
Established
MIB_TCP_STATE_FIN_WAIT1
==
Waiting for FIN
MIB_TCP_STATE_FIN_WAIT2
==
Waiting for FIN
MIB_TCP_STATE_CLOSE_WAIT
==
Waiting for Close
MIB_TCP_STATE_CLOSING
==
Closing
MIB_TCP_STATE_LAST_ACK
==
Last ACK
MIB_TCP_STATE_TIME_WAIT
==
Time Wait
MIB_TCP_STATE_DELETE_TCB
==
TCB deleted