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;
}