http://www.tanhp.com/index.php/archives/219/
默认情况下,新建的Socket是阻塞模式的。可以调用 ioctlsocket()
函数将Socket设置为非阻塞模式,函数原型如下:
int ioctlsocket(SOCKET s,long cmd,u_long* argp)
参数说明:
s
:Socket句柄;
cmd
:在 s
上执行的命令;
argp
:指针变量,指定 cmd
命令的参数,当 cmd
设为 FIONBIO
时, argp
非0,表示启用非阻塞模式。
1、非阻塞模式服务器
(1)接收来自客户端请求
由于启用非阻塞模式, accept
函数调用后会立即返回,无论是否有客户连接,所以需要每隔一段时间调用探测。
client=accept(server,(sockaddr*)&cAddr,&len);
if(client==INVALID_SOCKET)
{
int err=WSAGetLastError();
// 当前socket被表示为非阻塞,没有连接
if(err==WSAEWOULDBLOCK)
{
Sleep(100);
continue;
}
else
{
cout<<"accept error"<<endl;
closesocket(server);
WSACleanup();
return -1;
}
}
(2)接收数据 同样的,接收数据、发送数据也要循环探测,知道成功为止
while(true)
{
// 接收数据[循环测试]
ZeroMemory(buf,BUF_SIZE);
retVal=recv(client,buf,BUF_SIZE,0);
if(retVal==SOCKET_ERROR)
{
int err=WSAGetLastError();
if(err==WSAEWOULDBLOCK)
{
Sleep(100);
continue;
}
else
if(err==WSAETIMEDOUT||err==WSAENETDOWN)
{
closesocket(server);
closesocket(client);
WSACleanup();
return -1;
}
}
cout<<"Recv data:"<<buf<<endl;
(3)发送数据
while(true)
{
retVal=send(client,buf,sizeof(buf),0);
if(retVal==SOCKET_ERROR)
{
int err=WSAGetLastError();
if(err==WSAEWOULDBLOCK)
{
Sleep(100);
continue;
}
else
{
closesocket(server);
closesocket(client);
WSACleanup();
return -1;
}
}
break;
}
}
2、非阻塞模式客户端
同非阻塞服务器
3、非阻塞模式多线程服务器
上面介绍的服务器应用程序是单线程的,也就是说服务器在同一时刻只能与一个客户进行通信,这显然无法满足实际应用需求。
我们设计这样一个多线程服务器,主线程(即 main()
函数)负责介绍来自客户的连接请求,然后创建专门与客户端进行通信的子线程。
while(true)
{
client=accept(server,(sockaddr*)&caddr,&len);
if(client==INVALID_SOCKET)
{
int err=WSAGetLastError();
if(err==WSAEWOULDBLOCK)
{
Sleep(100);
continue;
}
else
{
return -1;
}
}
else
{
// 创建新线程
tmpSocket[i]=client;
_beginthreadex(NULL,0,AnswerThread,&tmpSocket[i],0,NULL);
}
}
特别需要注意的是,每次创建线程时传入的 SOCKET
指针不要使用全局的 client
,需要另外创建 tmpSocket
数组保存每次连接,这样避免了多个客户端连接多同一个 client
进行修改。
或者将 SOCKET
强制转换为 void *
,这样可以直接将 client
作为实参调用。
_beginthreadex(NULL,0,AnswerThread,(void*)client,0,NULL); // 调用
SOCKET client=(SOCKET)lp; // 线程函数