最近在做一个服务器端程序,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 说明,很详细,很有帮助。
这种方法经过长时间测试后,是有效的。所以写出来让大家参考一下,请大家发表意见。

参考:http://www.cppblog.com/prayer/archive/2009/04/14/79900.aspx

tcp会自动断开连接吗?

已经建立了TCP连接,并可能互通信息。但是如果长时间不进行信息的传递。这个TCP连接会自动断开吗?
如果能自动断开的话,这个时间大约是多少呢?

回答:TCP的保活定时器能够保证TCP连接一直保持,但是TCP的保活定时器不是每个TCP/IP协议栈就实现了,因为RFC并不要求TCP保活定时器一定要实现。

摘自《TCP/IP详解》卷1第23章:保活并不是T C P规范中的一部分。Host Requirements RFC提供了3个不使用保活定
时器的理由: (1) 在出现短暂差错的情况下,这可能会使一个非常好的连接释放掉;
(2)它们耗费不必要的带宽;(3)在按分组计费的情况下会在互联网上花掉更多的钱。
然而,许多实现提供了保活定时器。

更具体的资料,请参阅RFC。

tcp/ip详解更全面的描述:

tcp保活定时器

23.1介绍

在一个空闲的(idle)TCP连接上,没有任何的数据流,许多TCP/IP的初学者都对此感到惊奇。也就是说,如果TCP连接两端没有任何一个进程在向对方发送数据,那么在这两个TCP模块之间没有任何的数据交换。你可能在其它的网络协议中发现有轮询(polling),但在TCP中它不存在。言外之意就是我们只要启动一个客户端进程,同服务器建立了TCP连接,不管你离开几小时,几天,几星期或是几个月,连接依旧存在。中间的路由器可能崩溃或者重启,电话线可能go down或者back up,只要连接两端的主机没有重启,连接依旧保持建立。

这就可以认为不管是客户端的还是服务器端的应用程序都没有应用程序级(application-level)的定时器来探测连接的不活动状态(inactivity),从而引起任何一个应用程序的终止。回忆在10.7结束,BGP每隔30秒就向对方发送一个应用程序探测。这是一个应用程序定时器(application timer),与TCP存活定时器不同。

然而有的时候,服务器需要知道客户端主机是否已崩溃并且关闭,或者崩溃但重启。许多实现提供了存活定时器来完成这个任务。

存活(keepalive)并不是TCP规范的一部分。在Host Requirements RFC罗列有不使用它的三个理由:(1)在短暂的故障期间,它们可能引起一个良好连接(good connection)被释放(dropped),(2)它们消费了不必要的宽带,(3)在以数据包计费的互联网上它们(额外)花费金钱。然而,在许多的实现中提供了存活定时器。

存活定时器是一个包含争议的特征。许多人认为,即使需要这个特征,这种对对方的轮询也应该由应用程序来完成,而不是由TCP中实现。一些人对这个话题表现了极大的热情,甚至达到宗教般的狂热。

如果两个终端系统之间的某个中间网络上有连接的暂时中断,那么存活选项(option)就能够引起两个进程间一个良好连接的终止。例如,如果正好在某个中间路由器崩溃、重启的时候发送存活探测,TCP就将会认为客户端主机已经崩溃,但事实并非如此。

一些服务器应用程序可能代表客户端占用资源,它们需要知道客户端主机是否崩溃。存活定时器可以为这些应用程序提供探测服务。Telnet服务器和Rlogin服务器的许多版本都默认提供存活选项。

个人计算机用户使用TCP/IP协议通过Telnet登录一台主机,这是能够说明需要使用存活定时器的一个常用例子。如果某个用户在使用结束时只是关掉了电源,而没有注销(log off),那么他就留下了一个半打开(half-open)的连接。在图18.16,我们看到如何在一个半打开连接上通过发送数据,得到一个复位(reset)返回,但那是在客户端,是由客户端发送的数据。如果客户端消失,留给了服务器端半打开的连接,并且服务器又在等待客户端的数据,那么等待将永远持续下去。存活特征的目的就是在服务器端检测这种半打开连接。

更多:http://blog.csdn.net/zhoujunyi/article/details/1920871

我的方法不一样,我用getsockopt来判断,还是蛮准确的

  1. int SocketConnected(int sock)
  2. {
  3. if(sock<=0)
  4. return 0;
  5. struct tcp_info info;
  6. int len=sizeof(info);
  7. getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
  8. if((info.tcpi_state==TCP_ESTABLISHED))
  9. {
  10. //myprintf("socket connected\n");
  11. return 1;
  12. }
  13. else
  14. {
  15. //myprintf("socket disconnected\n");
  16. return 0;
  17. }
  18. }

tcp_info和TCP_ESTABLISHED在linux/tcp.h

包含

#include <linux/types.h>
#include <asm/byteorder.h>
#include <linux/config.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <net/sock.h>

http://www.cse.scu.edu/~dclark/am_256_graph_theory/linux_2_6_stack/globals.html#index_t

int SocketConnected(int sock) 

if(sock<=0) 
return 0; 
struct tcp_info info; 
int len=sizeof(info); 
getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len); 
if((info.tcpi_state==TCP_ESTABLISHED)) 

//myprintf("socket connected\n"); 
return 1; 

else 

//myprintf("socket disconnected\n"); 
return 0; 

}

目前主要有三种方法来实现用户掉线检测: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:

DWORD dwError = 0L ;

tcp_keepalive sKA_Settings = {0}, sReturned = {0} ;

sKA_Settings.onoff = 1 ;

sKA_Settings.keepalivetime = 5500 ; // Keep Alive in 5.5 sec.

sKA_Settings.keepaliveinterval = 3000 ; // Resend if No-Reply

if (WSAIoctl(skNewConnection, SIO_KEEPALIVE_VALS, &sKA_Settings,

sizeof(sKA_Settings), &sReturned, sizeof(sReturned), &dwBytes,

NULL, NULL) != 0)

{

dwError = WSAGetLastError() ;

}

实现时需要添加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包,客户端受到该包后,立刻返回相应的反馈 包。

该方法的好处是通用,但缺点就是会改变现有的通讯协议!

/* Net check Make sure you have not used OUT OF BAND DATA AND YOU CAN use OOB */
int netcheck(int fd)
{int buf_size = 1024;char buf[buf_size];//clear OOB DATA recv(fd, buf, buf_size);if(send(fd, (void *)"\0", 1, MSG_OOB) < 0 ){fprintf(stderr, "Connection[%d] send OOB failed, %s", fd, strerror(errno));return -1;}return 0;
}

/* Setting SO_TCP KEEPALIVE */
//int keep_alive = 1;//设定KeepAlive
//int keep_idle = 1;//开始首次KeepAlive探测前的TCP空闭时间
//int keep_interval = 1;//两次KeepAlive探测间的时间间隔
//int keep_count = 3;//判定断开前的KeepAlive探测次数
void set_keepalive(int fd, int keep_alive, int keep_idle, int keep_interval, int keep_count)
{int opt = 1;if(keep_alive){if(setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,(void*)&keep_alive, sizeof(keep_alive)) == -1){fprintf(stderr, "setsockopt SOL_SOCKET::SO_KEEPALIVE failed, %s\n",strerror(errno));}if(setsockopt(fd, SOL_TCP, TCP_KEEPIDLE,(void *)&keep_idle,sizeof(keep_idle)) == -1){fprintf(stderr,"setsockopt SOL_TCP::TCP_KEEPIDLE failed, %s\n", strerror(errno));}if(setsockopt(fd,SOL_TCP,TCP_KEEPINTVL,(void *)&keep_interval, sizeof(keep_interval)) == -1){fprintf(stderr,"setsockopt SOL_tcp::TCP_KEEPINTVL failed, %s\n", strerror(errno));}if(setsockopt(fd,SOL_TCP,TCP_KEEPCNT,(void *)&keep_count,sizeof(keep_count)) == -1){fprintf(stderr, "setsockopt SOL_TCP::TCP_KEEPCNT failed, %s\n", strerror(errno));}}
}

一篇文章:

keep alive VS heart beart:

这周在上班的路上看了本书《Effective TCP/IP Programming》,以下是一些读书笔记。顺带推荐一下这本书,写的很棒,适用于像我这样经常要写一些有一定质量的网络编程,但又没时间啃那些讲解TCPIP协议大部头书的人。

很多人都知道TCP并不会去主动检测连接的丢失,这意味着,如果双方不产生交互,那么如果网络断了或者有一方机器崩溃,另外一方将永远不知道连接已经不可用了。检测连接是否丢失的方法大致有两种:keepalive和heart-beat。

Keepalive是很多的TCP实现提供的一种机制,它允许连接在空闲的时候双方会发送一些特殊的数据段,并通过响应与否来判断连接是否还存活着(所谓keep~~alive)。我曾经写过一篇关于keepalive的blog ,但后来我也发现,其实keepalive在实际的应用中并不常见。为何如此?这得归结于keepalive设计的初衷。Keepalive适用于清除死亡时间比较长的连接。 
比如这样的场景:一个用户创建tcp连接访问了一个web服务器,当用户完成他执行的操作后,很粗暴的直接拨了网线。这种情况下,这个tcp连接已经断开了,但是web服务器并不知道,它会依然守护着这个连接。如果web server设置了keepalive,那么它就能够在用户断开网线的大概几个小时以后,确认这个连接已经中断,然后丢弃此连接,回收资源。
采用keepalive,它会先要求此连接一定时间没有活动(一般是几个小时),然后发出数据段,经过多次尝试后(每次尝试之间也有时间间隔),如果仍没有响应,则判断连接中断。可想而知,整个周期需要很长的时间。
所以,如前面的场景那样,需要一种方法能够清除和回收那些在系统不知情的情况下死去了很久的连接,keepalive是非常好的选择。 
但是,在大部分情况下,特别是分布式环境中,我们需要的是一个能够快速或者实时监控连接状态的机制,这里,heart-beat才是更加合适的方案。 
Heart-beat(心跳),按我的理解,它的原理和keepalive非常类似,都是发送一个信号给对方,如果多次发送都没有响应的话,则判断连接中断。它们的不同点在于,keepalive是tcp实现中内建的机制,是在创建tcp连接时通过设置参数启动keepalive机制;而heart-beat则需要在tcp之上的应用层实现。一个简单的heart-beat实现一般测试连接是否中断采用的时间间隔都比较短,可以很快的决定连接是否中断。并且,由于是在应用层实现,因为可以自行决定当判断连接中断后应该采取的行为,而keepalive在判断连接失败后只会将连接丢弃。
关于heart-beat,一个非常有趣的问题是,应该在传输真正数据的连接中发送“心跳”信号,还是可以专门创建一个发送“心跳”信号的连接。比如说,A,B两台机器之间通过连接m来传输数据,现在为了能够检测A,B之间的连接状态,我们是应该在连接m中传输“心跳”信号,还是创建新的连接n来专门传输“心跳”呢?我个人认为两者皆可。如果担心的是端到端的连接状态,那么就直接在该条连接中实现“心跳”。但很多时候,关注的是网络状况和两台主机间的连接状态,这种情况下, 创建专门的“心跳”连接也未尝不可。

tcp 服务端如何判断客户端断开连接相关推荐

  1. netty获取玩家chanel_基于netty的TCP服务端如何给客户端发送消息,但是如何拿到客户端连接时的SocketChannel呢,菜鸟求助?...

    1.思路1 每个客户端连接时的SocketChannel保存在会话类sessionManager中的sessionIdMap中 问题: 1.客户端连接时确实将SocketChannel保存在会话类se ...

  2. Socket服务端向指定客户端发送消息

    Socket服务端向指定客户端发送消息 解决思想 1.项目背景 2.如何上传与下发指令 3.解决方法 4.流程 解决思想 I.指定客户端远程地址是存起来的. II.服务端直接主动发信息给客户端,问题在 ...

  3. 服务端挂了,客户端的TCP连接还会在吗?

    一.服务端进程崩溃,客户端会发生什么? TCP 的连接信息是由内核维护的,所以当服务端的进程崩溃后,内核需要回收该进程的所有 TCP 连接资源,于是内核会发送第一次挥手 FIN 报文,后续的挥手过程也 ...

  4. java BIO tcp服务端向客户端消息群发代码教程实战

    前言 项目需要和第三方厂商的服务需要用TCP协议通讯,考虑到彼此双方可能都会有断网重连.宕机重启的情况,需要保证 发生上述情况后,服务之间能够自动实现重新通信.研究测试之后整理如下代码实现.因为发现客 ...

  5. .NET TCP服务端和客户端

    客户端 public class TcpClientHelper{/// <summary>/// 是否连接上/// </summary>public bool Connect ...

  6. python学习随堂笔记—TCP服务端与客户端

    思想: 1. 客户端 向服务端 发起连接 2. 服务端 接到请求,双方建立连接 3. 客户端 向 服务端发消息 4. 服务端 应答客户端 5. 服务端 与客户端循环读写操作 6. 操作完成之后客户端发 ...

  7. Qt 多线程TCP服务端一键关闭所有客户端

    Qt 多线程TCP服务端一键关闭所有客户端 任务描述: 实现多线程TCP服务端一键关闭所有客户端的连接. 解决过程: 1.Qt的服务端提供了close的功能,但是只用来不响应新接入的客户端. 手册中是 ...

  8. MFC:Socket编程—TCP服务端和多个客户端通信

    前言 MFC是微软基础类库,于 C++ 对于 C语言来说,MFC对于window API ,MFC 就相当于C++,window API 相当于C.MFC 封装了 window API 使用起来更加的 ...

  9. Qt:Qt实现Winsock网络编程—TCP服务端和客户端通信(多线程)

    Qt实现Winsock网络编程-TCP服务端和客户端通信(多线程) 前言 感觉Winsock网络编程的api其实和Linux下网络编程的api非常像,其实和其他编程语言的网络编程都差不太多.博主用Qt ...

最新文章

  1. 支持向量机(SVM)算法
  2. Windows 下面的 redis GUI操作工具
  3. (Excel)常用函数公式及操作技巧之四:文本与页面设置(二)
  4. php 数组 指针,php之数组指针详解
  5. Linux快捷键及文件和目录的操作
  6. java 工厂模式的写法_java简单工厂模式是什么
  7. gorm利用钩子函数BeforeUpdate更新某个字段
  8. 架构设计工作笔记002---微服务架构理论-扩展立方体
  9. logrotate日志轮转策略
  10. mysql基本概念之关系_Mysql数据库学习(一):数据库基本概念、关系型数据库、Mysql数_MySQL...
  11. 前端开发的几种常用的编辑器
  12. bubblesort java,算法bubbleSort()
  13. [ P1 ] 雷达相机标定基本知识
  14. T检验、卡方检验、F检验
  15. 共青团课程华育兴业java_华育兴业|Java程序员金三银四必备面试题及答案(基础篇)...
  16. Java 使用 POI 3.17根据Word 模板替换、操作书签
  17. BatchNorm和LayerNorm的比较
  18. Grbl v1.1版本的编译方法
  19. linux信号解释(1)
  20. python苹果下载软件_PythonforMac官方下载_PythonforMac最新版_PythonforMac3.6.4官方最新版-华军软件园...

热门文章

  1. python flask快速入门与进阶 百度云_Python Flask快速入门与进阶
  2. android字体显示不全,android 按钮的文字显示不全
  3. php5.4开启opcache,windows下php5.6启用opcache
  4. idle扩展插件_Python3.4学习笔记之 idle 清屏扩展插件用法分析
  5. python字典dragonloot_Python 字典与列表使用实例
  6. java 多模块项目 包路径冲突_多智能体仿真建模在交通中的应用|MATSim入门指南...
  7. 拆解 | 某平台19元的儿童电话手表,究竟怎么做到的?
  8. 为什么要嫁就嫁电力男!
  9. 又涨了?2021 年 3 月程序员工资统计新出炉
  10. 要不要出去找一份实习?