非阻塞套接字的模式
1
.非阻塞套接字的模式
( 1 )服务器端
通常socket运行后默认为阻塞模式。要调用ioctlsocket函数设置非阻塞模式。
如:
WSAData Data;
WSAStartup(MAKEWORD( 2 , 2 ), & Data);
SerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == SerSocket)
cout << " Invalid Socket!\n " ;
u_long iMode = 1 ;
ioctlsocket(SerSocket, FIONBIO, & iMode);
在接受客户端请求的线程中,若接受成功就返回客户端的套接字,否则返回INVALID_SOCKET错误,
若错误代码为WSAEWOULDBLOCK,说明当前没有客户端请求。
如:
// 接受客户端请求线程
DWORD WINAPI AcceptClientPro(LPVOID LpP)
{
SOCKADDR_IN ClientAdrr;
int AddrLen = sizeof (SOCKADDR);
// 非阻塞模式
while ( ! IsConnet)
{
ClientSock = accept(SerSocket, (SOCKADDR * ) & ClientAdrr, & AddrLen );
if (INVALID_SOCKET == ClientSock)
{
int n = WSAGetLastError();
// 没有客户端请求
if (WSAEWOULDBLOCK == n)
{
cout << " 没有客户端发出请求! " << endl;
Sleep( 1000 );
continue ;
} else
{
cout << " 出现错误! " << endl;
Sleep( 1000 );
}
} else
{
cout << " 已连接客户端! " << endl;
IsConnet = true ;
break ;
}
}
return 0 ;
}
就recv函数来说,在阻塞模式中,如果没有客户端发送数据过来,线程到这里会阻塞,直到有数
据发送过来为止。在非阻塞模式中,没有客户端发送数据过来,返回SOCKER_ERROR,错误代码为WSAEWOULDBLOCK。
如:
// 接收数据线程
DWORD WINAPI ReceiveDataPro(LPVOID LpP)
{
while ( ! IsConnet); // 保证连接后再接受数据
while ( 1 )
{
if (IsReadyRecei) // 保证缓冲区在未处理时不受新来的数据的影响
{
int ReceiLen = recv(ClientSock, ( char * ) & DataPack, sizeof (DataPack), 0 );
if (SOCKET_ERROR == ReceiLen)
{
int Err = WSAGetLastError();
if (WSAEWOULDBLOCK == Err)
{
cout << " 没有收到数据 " << endl;
continue ;
}
else if (WSAENETDOWN == Err || // 客户端关闭了连接
WSAETIMEDOUT == Err ||
WSAECONNRESET == Err )
{
cout << " 服务器关闭了连接 " << endl;
break ;
}
}
if ( 0 == ReceiLen) // 客户端关闭了连接
{
cout << " ReceiLen = 0 " << endl;
break ;
}
if (ReceiLen >= sizeof (DataPack)) // 成功接收
{
cout << " 已收到数据: " << DataPack.buf << endl;
IsReadyRecei = false ;
break ;
}
}
}
return 0 ;
}
( 2 )客户端
在客户端的连接请求线程中,connect函数会返回SOCKET_ERROR,这并不是说明连接失败,具体情况
要看它的WSAGetLastError()返回值,若它三次返回SOCKET_ERROR的Error代码依次为WSAEWOULDBLOCK,
WSAEINVAL,WSAEISCONN,就说明连接服务器成功,否则失败。但有的时候WSAEINVAL没有出现就有WSAEISCONN
了,所以我还是以WSAEISCONN为连接完成的标志,但要注意其实在三次返回代码中,第一次的WSAEWOULDBLOCK
之前的connect操作就成功了,如果没出意外服务器就要响应了。
如:
// 连接服务器线程
DWORD WINAPI ConnetServerPro(LPVOID LpP)
{
SOCKADDR_IN ServerAddr;
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons( 1200 );
ServerAddr.sin_addr.s_addr = inet_addr( " 192.168.1.100 " );
int BlockFlag = 0 ;
int InvalFlag = 0 ;
while ( ! IsConnet)
{
int nResu = connect(ClientSock, (SOCKADDR * ) & ServerAddr, sizeof (SOCKADDR));
if (SOCKET_ERROR == nResu)
{
int n = WSAGetLastError();
if (WSAEWOULDBLOCK == n ) // 不能立即完成
{
cout << " 过程1! " << endl;
BlockFlag ++ ;
continue ;
}
else if (WSAEINVAL == n) // 监听状态
{
cout << " 过程2! " << endl;
InvalFlag ++ ;
continue ;
}
else if (WSAEISCONN == n) // 连接完成
{
cout << " 已连接服务器! " << endl;
IsConnet = true ;
break ;
}
else
{
cout << " 出现其他错误!\n " << endl;
Sleep( 1000 );
}
}
}
return 0 ;
}
在发送数据线程中,send()返回的是发送数据的长度说明发送成功;返回SOCKET_ERROR时,若
错误代码为WSAEWOULDBLOCK就再重试,不是WSAEWOULDBLOCK就说明有错误。】
如:
// 发送数据线程
DWORD WINAPI SendDataPro(LPVOID LpP)
{
while ( ! IsConnet); // 保证已连接服务器
while ( 1 )
{
int len = send(ClientSock, ( char * ) & DataPack, DataPack.Head.len, 0 );
if (SOCKET_ERROR == len)
{
int Error = WSAGetLastError();
if (WSAEWOULDBLOCK == Error)
continue ;
}
else // 发送成功
{
cout << " 发送成功! " << endl;
break ;
}
}
return 0 ;
}
( 1 )服务器端
通常socket运行后默认为阻塞模式。要调用ioctlsocket函数设置非阻塞模式。
如:
WSAData Data;
WSAStartup(MAKEWORD( 2 , 2 ), & Data);
SerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == SerSocket)
cout << " Invalid Socket!\n " ;
u_long iMode = 1 ;
ioctlsocket(SerSocket, FIONBIO, & iMode);
在接受客户端请求的线程中,若接受成功就返回客户端的套接字,否则返回INVALID_SOCKET错误,
若错误代码为WSAEWOULDBLOCK,说明当前没有客户端请求。
如:
// 接受客户端请求线程
DWORD WINAPI AcceptClientPro(LPVOID LpP)
{
SOCKADDR_IN ClientAdrr;
int AddrLen = sizeof (SOCKADDR);
// 非阻塞模式
while ( ! IsConnet)
{
ClientSock = accept(SerSocket, (SOCKADDR * ) & ClientAdrr, & AddrLen );
if (INVALID_SOCKET == ClientSock)
{
int n = WSAGetLastError();
// 没有客户端请求
if (WSAEWOULDBLOCK == n)
{
cout << " 没有客户端发出请求! " << endl;
Sleep( 1000 );
continue ;
} else
{
cout << " 出现错误! " << endl;
Sleep( 1000 );
}
} else
{
cout << " 已连接客户端! " << endl;
IsConnet = true ;
break ;
}
}
return 0 ;
}
就recv函数来说,在阻塞模式中,如果没有客户端发送数据过来,线程到这里会阻塞,直到有数
据发送过来为止。在非阻塞模式中,没有客户端发送数据过来,返回SOCKER_ERROR,错误代码为WSAEWOULDBLOCK。
如:
// 接收数据线程
DWORD WINAPI ReceiveDataPro(LPVOID LpP)
{
while ( ! IsConnet); // 保证连接后再接受数据
while ( 1 )
{
if (IsReadyRecei) // 保证缓冲区在未处理时不受新来的数据的影响
{
int ReceiLen = recv(ClientSock, ( char * ) & DataPack, sizeof (DataPack), 0 );
if (SOCKET_ERROR == ReceiLen)
{
int Err = WSAGetLastError();
if (WSAEWOULDBLOCK == Err)
{
cout << " 没有收到数据 " << endl;
continue ;
}
else if (WSAENETDOWN == Err || // 客户端关闭了连接
WSAETIMEDOUT == Err ||
WSAECONNRESET == Err )
{
cout << " 服务器关闭了连接 " << endl;
break ;
}
}
if ( 0 == ReceiLen) // 客户端关闭了连接
{
cout << " ReceiLen = 0 " << endl;
break ;
}
if (ReceiLen >= sizeof (DataPack)) // 成功接收
{
cout << " 已收到数据: " << DataPack.buf << endl;
IsReadyRecei = false ;
break ;
}
}
}
return 0 ;
}
( 2 )客户端
在客户端的连接请求线程中,connect函数会返回SOCKET_ERROR,这并不是说明连接失败,具体情况
要看它的WSAGetLastError()返回值,若它三次返回SOCKET_ERROR的Error代码依次为WSAEWOULDBLOCK,
WSAEINVAL,WSAEISCONN,就说明连接服务器成功,否则失败。但有的时候WSAEINVAL没有出现就有WSAEISCONN
了,所以我还是以WSAEISCONN为连接完成的标志,但要注意其实在三次返回代码中,第一次的WSAEWOULDBLOCK
之前的connect操作就成功了,如果没出意外服务器就要响应了。
如:
// 连接服务器线程
DWORD WINAPI ConnetServerPro(LPVOID LpP)
{
SOCKADDR_IN ServerAddr;
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons( 1200 );
ServerAddr.sin_addr.s_addr = inet_addr( " 192.168.1.100 " );
int BlockFlag = 0 ;
int InvalFlag = 0 ;
while ( ! IsConnet)
{
int nResu = connect(ClientSock, (SOCKADDR * ) & ServerAddr, sizeof (SOCKADDR));
if (SOCKET_ERROR == nResu)
{
int n = WSAGetLastError();
if (WSAEWOULDBLOCK == n ) // 不能立即完成
{
cout << " 过程1! " << endl;
BlockFlag ++ ;
continue ;
}
else if (WSAEINVAL == n) // 监听状态
{
cout << " 过程2! " << endl;
InvalFlag ++ ;
continue ;
}
else if (WSAEISCONN == n) // 连接完成
{
cout << " 已连接服务器! " << endl;
IsConnet = true ;
break ;
}
else
{
cout << " 出现其他错误!\n " << endl;
Sleep( 1000 );
}
}
}
return 0 ;
}
在发送数据线程中,send()返回的是发送数据的长度说明发送成功;返回SOCKET_ERROR时,若
错误代码为WSAEWOULDBLOCK就再重试,不是WSAEWOULDBLOCK就说明有错误。】
如:
// 发送数据线程
DWORD WINAPI SendDataPro(LPVOID LpP)
{
while ( ! IsConnet); // 保证已连接服务器
while ( 1 )
{
int len = send(ClientSock, ( char * ) & DataPack, DataPack.Head.len, 0 );
if (SOCKET_ERROR == len)
{
int Error = WSAGetLastError();
if (WSAEWOULDBLOCK == Error)
continue ;
}
else // 发送成功
{
cout << " 发送成功! " << endl;
break ;
}
}
return 0 ;
}