如何判断SOCKET已经断开

摘要:
使用select()函数测试套接字是否可读时,如果select()的返回值为1,recv()函数读取的数据长度为0,则表示套接字已断开连接。为了更好地确定套接字是否已断开,我判断当recv()返回值小于或等于0时,套接字连接已断开。然而,还需要确定errno是否等于EINTR。SIO设置为完成端口的套接字_ KEEPALIVE_ VALS之后,TCPSTACK负责激活程序包。当网络连接断开时,TCPSTACK不会主动通知上层应用程序,而是通知下一个REC
http://biancheng.dnbcw.info/linux/366100.html

    最近在做一个服务器端程序,C/S结构。功能方面比较简单就是client端与server端建立连接,然后发送消息给server。我在server端会使用专门的线程处理一条socket连接。这就涉及到一个问题,如果socket连接断开(异常,正常)后,我如何才能感知到?server端这边是绝对被动的,sever端不能主动断开连接。也没有连接链路维持包之类的。client端发送数据的时间也是不定的。在socket连接断开后, server要能够感知到并释放资源。
    这个问题在思考测试,询问同事之后,找到了一个方法,可以做到这一点。
    当使用 select()函数测试一个socket是否可读时,如果select()函数返回值为1,且使用recv()函数读取的数据长度为0 时,就说明该socket已经断开。
    为了更好的判定socket是否断开,我判断当recv()返回值小于等于0时,socket连接断开。但是还需要判断 errno是否等于 EINTR 。如果errno == EINTR 则说明recv函数是由于程序接收到信号后返回的,socket连接还是正常的,不应close掉socket连接。
    PS:对于堵塞socket的recv函数会在以下三种情况下返回:
    (1)recv到数据时,会返回。
    (2)在整个程序接收到信号时,返回-1。errno = EINTR。//在程序的起始阶段,屏蔽掉信号的除外。部分信号还是屏蔽不掉的。
    (3)socket出现问题时,返回-1.具体错误码看 man recv()
    (4)一定要看 man 说明,很详细,很有帮助。
    这种方法经过长时间测试后,是有效的。所以写出来让大家参考一下,请大家发表意见。
    我的方法不一样,我用getsockopt来判断,还是蛮准确的

点击(此处)折叠或打开

  1. int SocketConnected(int sock)
  2. {
  3.     if(sock <= 0)
  4.         return 0;
  5.     struct tcp_info info;
  6.     intlen= sizeof(info);
  7.     getsockopt(sock, IPPROTO_TCP, TCP_INFO,&info,(socklen_t *)&len);
  8.     if((info.tcpi_state == TCP_ESTABLISHED)){
  9.         //myprintf("socket connected ");
  10.         return 1;
  11.     }else{
  12.         //myprintf("socket disconnected ");
  13.         return 0;
  14.     }
  15. }
tcp_info和TCP_ESTABLISHED在linux/tcp.h
包含

点击(此处)折叠或打开

  1. #include <linux/types.h>
  2. #include <asm/byteorder.h>
  3. #include <linux/config.h>
  4. #include <linux/skbuff.h>
  5. #include <linux/ip.h>
  6. #include <net/sock.h>
  7. http:                //www.cse.scu.edu/~dclark/am_256_graph_theory/linux_2_6_stack/globals.html#index_t
  8. int SocketConnected(int sock)
  9. {
  10.     if(sock <= 0)
  11.         return 0;
  12.     struct tcp_info info;
  13.     intlen= sizeof(info);
  14.     getsockopt(sock, IPPROTO_TCP, TCP_INFO,&info,(socklen_t *)&len);
  15.     if((info.tcpi_state == TCP_ESTABLISHED)){
  16. //myprintf("socket connected ");
  17.         return 1;
  18.     }else{
  19. //myprintf("socket disconnected ");
  20.         return 0;
  21.     }
  22. }
    目前主要有三种方法来实现用户掉线检测:SO_KEEPALIVE ,SIO_KEEPALIVE_VALS 和Heart-Beat线程。
    下面我就上面的三种方法来做一下介绍。
    (1)SO_KEEPALIVE 机制
    这是socket库提供的功能,设置接口是setsockopt API:
    BOOL bSet=TRUE;
    setsockopt(hSocket,SOL_SOCKET,SO_KEEPALIVE,(const char*)&bSet,sizeof(BOOL));
    根据MSDN的文档,如果为socket设置了KEEPALIVE选项,TCP/IP栈在检测到对方掉线后,
    任何在该socket上进行的调用(发送/接受调用)就会立刻返回,错误号是WSAENETRESET ;
    同时,此后的任何在该socket句柄的调用会立刻失败,并返回WSAENOTCONN错误。
    该机制的缺点也很明显:
    默认设置是空闲2小时才发送一个“保持存活探测分节”,不能保证实时检测!
    当然也可以修改时间间隔参数,但是会影响到所有打开此选项的套接口!
    关联了完成端口的socket可能会忽略掉该套接字选项。
    (2)SIO_KEEPALIVE_VALS 机制
    设置接口是WSAIoctl API:

点击(此处)折叠或打开

  1. DWORD dwError = 0L ;
  2. tcp_keepalive sKA_Settings ={0}, sReturned ={0};
  3. sKA_Settings.onoff = 1 ;
  4. sKA_Settings.keepalivetime = 5500 ;// Keep Alive in 5.5 sec.
  5. sKA_Settings.keepaliveinterval = 3000 ;// Resend if No-Reply
  6. if(WSAIoctl(skNewConnection, SIO_KEEPALIVE_VALS,&sKA_Settings,
  7.           sizeof(sKA_Settings),&sReturned, sizeof(sReturned),&dwBytes,
  8.           NULL,NULL)!= 0)
  9. {
  10.     dwError = WSAGetLastError();
  11. }
     实现时需要添加tcp_keepalive and SIO_KEEPALIVE_VALS的定义文件MSTCPiP.h
     该选项不同于SO_KEEPALIVE 机制的就是它是针对单个连接的,对系统其他的套接
     口并不影响。
        针对完成端口的socket,设置了SIO_KEEPALIVE_VALS后,激活包由TCP STACK来负责。
     当网络连接断开后,TCP STACK并不主动告诉上层的应用程序,但是当下一次RECV或者SEND操作
     进行后,马上就会返回错误告诉上层这个连接已经断开了.如果检测到断开的时候,在这个连接
     上有正在PENDING的IO操作,则马上会失败返回.
     该机制的缺点:
             不通用啦。MS的API只能用于Windows拉。不过,优雅一些^_^.
    
(3)Heart-Beat线程
        没说的。自己写一个后台线程,实现Heart-Beat包,客户端受到该包后,立刻返回相应的反馈 包。
    该方法的好处是通用,但缺点就是会改变现有的通讯协议!

点击(此处)折叠或打开

  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 *)"

免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇线程池如何传递ThreadLocalCentos7 忘记密码的情况下,修改root或其他用户密码下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Socket 编程中,TCP 流的结束标志与粘包问题

因为 TCP 本身是无边界的协议,因此它并没有结束标志,也无法分包。「包」的界定,是更上层的协议的事了(比如 HTTP)。 socket和文件不一样,从文件中读,读到末尾就到达流的结尾了,所以会返回-1或null,循环结束,但是socket是连接两个主机的桥梁,一端无法知道另一端到底还有没有数据要传输。socket如果不关闭的话,read之类的阻塞函数会一...

JavaEE简介

1、为什么需要JavaEE 我们编写的JSP代码中,由于大量的显示代码和业务逻辑混淆在一起,彼此嵌套,不利于程序的维护和扩展。当业务需求发生变化的时候,对于程序员和美工都是一个很重的负担。 为了程序的易维护性和可扩展性,这就需要我们使用JavaEE技术来进行项目开发 2、什么是JavaEE JavaEE是一个开发分布式企业级应用的规范和标准。 Java语言...

bss段为什么需要初始化?

    我们都知道bss段需要初始化,但是这是为什么呢?        通过浏览资料,我们都会发现,bss段是不会出现在程序下载文件(*.bin *.hex)中的,因为全都是0。如果把它们出现在程序下载文件中,会增加程序下载文件的大小。实际应用中,通常只需要把bss段的起始地址和结束地址保存起来,而不需要将程序下载文件中出现bss段(一堆0)将来真正运行程...

Centos6.5 设置nfs

安装 rpcbind 和 nfs-utils 1 yum install rpcbind 2 yum install nfs-utils 设置共享目录 [root@bogon ~]# vim /etc/exports #修改/etc/exports文件,输出共享目录 添加一行, 例如 /mnt/share *(rw,sync,n...

Code Tips: gcc对结构体的默认字节“对齐”方式

1. 发现问题     最近在编写代码过程中发现,对一个结构体进行 sizeof 操作时,有时候大小是填充过的,有时候又没有填充。     那么,如果在代码中没有显示的指定要求编译器进行对齐时,gcc的默认处理是怎样的呢? 2. 先说结论     代码中如果没有显示指定字节对齐时,gcc默认并没有进行cpu宽度字节对齐;     gcc会将结构体的大小填...

【转载】Iptables详解

参考链接:http://blog.csdn.net/reyleon/article/details/12976341 Iptabels是与Linux内核集成的包过滤防火墙系统,几乎所有的linux发行版本都会包含Iptables的功能。如果 Linux 系统连接到因特网或 LAN、服务器或连接 LAN 和因特网的代理服务器, 则Iptables有利于在 L...