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

 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值