linux socket

判断非阻塞SOCKET是否已经断开的几种方法

 

注意要区分不同操作系统分别进行测试, 包括WINDOWS, LINUX和UNIX会各有不同

 

在WINDOWS下比较简单,可以使用FD_CLOSE事件判断SOCKET是否已经断开

 

bool IsSocketClosed(SOCKET clientSocket)
{
 bool ret = false;
 HANDLE closeEvent = WSACreateEvent();
 WSAEventSelect(clientSocket, closeEvent, FD_CLOSE);

 DWORD dwRet = WaitForSingleObject(closeEvent, 0);
 
 if(dwRet == WSA_WAIT_EVENT_0)
  ret = true;
 else if(dwRet == WSA_WAIT_TIMEOUT)
  ret = false;

 WSACloseEvent(closeEvent);
 return ret;
}

 

 

在UNIX/LINUX下,非阻塞模式SOCKET可以采用recv+MSG_PEEK的方式进行判断,其中MSG_PEEK保证了仅仅进行状态判断,而不影响数据接收

对于主动关闭的SOCKET, recv返回-1,而且errno被置为9(#define EBADF   9 /* Bad file number */)

或104 (#define ECONNRESET 104 /* Connection reset by peer */)


对于被动关闭的SOCKET,recv返回0,而且errno被置为11(#define EWOULDBLOCK EAGAIN /* Operation would block */)

 

对正常的SOCKET, 如果有接收数据,则返回>0, 否则返回-1,而且errno被置为11(#define EWOULDBLOCK EAGAIN /* Operation would block */)

 

因此对于简单的状态判断(不过多考虑异常情况),
    recv返回>0,   正常
    返回-1,而且errno被置为11  正常
    其它情况    关闭

 

#include <errno.h>

bool IsSocketClosed(int clientSocket)
{
 char buff[32];
 int recvBytes = recv(clientSocket, buff, sizeof(buff), MSG_PEEK);
 
 int sockErr = errno;
 
 //cout << "In close function, recv " << recvBytes << " bytes, err " << sockErr << endl;
 
 if( recvBytes > 0) //Get data
  return false;
 
 if( (recvBytes == -1) && (sockErr == EWOULDBLOCK) ) //No receive data
  return false;
   
 return true;
}

 

此外,所有操作系统上还可以通过TCP_KEEPLIVE实行心跳检测


  1. /* Net check Make sure you have not used OUT OF BAND DATA AND YOU CAN use OOB */
  2. int netcheck(int fd) 
  3. {
  4.         int buf_size = 1024;
  5.         char buf[buf_size];
  6.         //clear OOB DATA 
  7.         recv(fd, buf, buf_size);
  8.         if(send(fd, (void *)"\0", 1, MSG_OOB) < 0 )
  9.         {
  10.                 fprintf(stderr, "Connection[%d] send OOB failed, %s", fd, strerror(errno));
  11.                 return -1;
  12.         }
  13.         return 0;
  14. }
复制代码

  1. /* Setting SO_TCP KEEPALIVE */
  2. //int keep_alive = 1;//设定KeepAlive
  3. //int keep_idle = 1;//开始首次KeepAlive探测前的TCP空闭时间
  4. //int keep_interval = 1;//两次KeepAlive探测间的时间间隔
  5. //int keep_count = 3;//判定断开前的KeepAlive探测次数
  6. void set_keepalive(int fd, int keep_alive, int keep_idle, int keep_interval, int keep_count)
  7. {
  8.         int opt = 1;
  9.         if(keep_alive)
  10.         {
  11.                 if(setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
  12.                                         (void*)&keep_alive, sizeof(keep_alive)) == -1)
  13.                 {
  14.                         fprintf(stderr, 
  15.                                 "setsockopt SOL_SOCKET::SO_KEEPALIVE failed, %s\n",strerror(errno));
  16.                 }
  17.                 if(setsockopt(fd, SOL_TCP, TCP_KEEPIDLE,
  18.                                         (void *)&keep_idle,sizeof(keep_idle)) == -1)
  19.                 {
  20.                         fprintf(stderr,
  21.                                 "setsockopt SOL_TCP::TCP_KEEPIDLE failed, %s\n", strerror(errno));
  22.                 }
  23.                 if(setsockopt(fd,SOL_TCP,TCP_KEEPINTVL,
  24.                                         (void *)&keep_interval, sizeof(keep_interval)) == -1)
  25.                 {
  26.                         fprintf(stderr,
  27.                                  "setsockopt SOL_tcp::TCP_KEEPINTVL failed, %s\n", strerror(errno));
  28.                 }
  29.                 if(setsockopt(fd,SOL_TCP,TCP_KEEPCNT,
  30.                                         (void *)&keep_count,sizeof(keep_count)) == -1)
  31.                 {
  32.                         fprintf(stderr, 
  33.                                 "setsockopt SOL_TCP::TCP_KEEPCNT failed, %s\n", strerror(errno));
  34.                 }
  35.         }
  36. }


下面来罗列一下判断远端已经断开的方法:

法一:

当recv()返回值小于等于0时,socket连接断开。但是还需要判断 errno是否等于 EINTR,如果errno == EINTR 则说明recv函数是由于程序接收到信号后返回的,socket连接还是正常的,不应close掉socket连接。

 

法二:

  struct tcp_info info; 
  int len=sizeof(info); 
  getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
  if((info.tcpi_state==TCP_ESTABLISHED))  则说明未断开  else 断开

 

法三:

若使用了select等系统函数,若远端断开,则select返回1,recv返回0则断开。其他注意事项同法一。

 

法四:

int keepAlive = 1; // 开启keepalive属性
int keepIdle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测
int keepInterval = 5; // 探测时发包的时间间隔为5 秒
int keepCount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.

setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));
setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));

设置后,若断开,则在使用该socket读写时立即失败,并返回ETIMEDOUT错误

 

法五:

自己实现一个心跳检测,一定时间内未收到自定义的心跳包则标记为已断开。

以上未测试,有空测试下


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值