高级Socket编程——阻塞与非阻塞模式Socket编程

来源:互联网 发布:为什么不能下载java 编辑:程序博客网 时间:2024/05/22 17:02

 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;                                 // 线程函数
0 0