TCP客户端如何快速判断与服务器断联
问题产生的背景说明
我曾经对接中国电信的服务器的时候,遇到了该问题。中国电信要求和服务器断联之后(异常或者正常断联,其中包括拔掉 线)能够在30s之内连接上中国电信服务器并且接收服务器下发的无线配置。客户端重启无线,能够用无线扫描工具扫描该无线ssid已经被同步。该需求当时面临如下几个问题:
1.客户端在和服务器连接的时候需要通过一系列的鉴权机制(dh秘钥协商),然后才可以和服务器通信
2. 无线配置到生效(ssid改变),需要花费一定的时间。
- 为了满足这一系列的动作,需要优化如下几点:
- 能够快速判断和服务器已经断开(正常或者异常)
- 必须优化无线生效时间,尽可能短
- 在拔掉 线再插上 线,必须尽快拿到分配的ip地址,尽快和服务器建立连接
今天我们来讨论如何快速的检测和服务器已经断联。
目前检测客户端和服务器断联的方法
- epoll(能够检测正常的断开连接,事件触发机制。优点是快速)
- read方式检测
- keeplive方式检测
- 自定义心跳包方式检测
- getsockopt
下面我们逐个分析上述方式的优缺点
epoll
epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。
相比较select和poll的方式,epoll对于监听的文件描述符没有限制(唯一的限制就是内核本身支持的文件描述符个数-可修改)。
我们在注册epoll的时候主要是监听两类事件(EPOLLIN和EPOLLOUT),这两类事件分别代表着可读和可写。我们可以创建读写回调函数,当检测到对应事件的时候,调用对应的程序。为了检测客户端和服务器已经断开我们需要注册另一类事件(EPOLLRDHUP)。该事件代表的是读关闭,当服务器close的时候,会触发该事件。
说明:EPOLLERR 只有采取动作时,才能知道是否对方异常。即对方突然断掉,是不可能有此事件发生的。只有自己采取动作(当然自己此刻也不知道),read,write时,出EPOLLERR错,说明对方已经异常断开。
总结: epoll的事件触发机制不满足上述需求(插拔 线是检测不到的)。但是它的优缺点不言而喻(文件描述符监听不受限制,事件触发)。
read方式检测
但我们不采用epoll的时候(即不采用事件触发机制的时候)。我们可以创建一个单独的读线程,文件描述符设置为非阻塞。那么在读线程中死循环调用read函数。这时候可以根据read的返回值来判断socket连接是否已经出了问题。检测方式如下:
调用了read函数读取socket,如果read的返回值为小于0同时错误码不为errno == EAGAIN || errno == EWOULDBLOCK),则代表和服务器的连接已经断开。
说明:该方式适用于各种异常情况,但是因为需要read循环接收,必须开辟线程。系统创建线程是需要有开销的,所以我不推荐该方式。
自定义心跳包检测方式
该方式对于大家都不陌生,说的直白一点就是定时发送心跳包给服务器。如果发送失败或者服务器没有定时回复,则认为连接已经断开。该方式通用、常见并且有效。在一些实时性要求不高的地方推荐这种方式。(基本现在的cs架构都有心跳机制)。但是该方式不适合我上述需求,因为心跳包是有间隔的,有间隔就代表有延时。同时又不能把心跳包设置的太短,这样会增加服务器负荷。
keeplive机制
关于keeplive机制,我在这里不详细说明。说白了就是利用系统发送心跳包。和上述心跳包检测方式一致
getsockopt机制
在应用程序中可以通过调用如下代码来判断连接是否断开
运行结果如下:

说明:该方法只适用于正常断开服务器(即服务器主动close),不适用于拔掉 线的情况
下面提供几种切换 络,断开 络的方案
arp方案
在客户端开启一个定时器,将定时事件设置很短,比如3秒。然后一直ping 关,设置ping的超时时间为1秒。如果检测到2次未返回(次数可以自己根据实际情况定义),则认为连接已经断开
说明: 该方案有一个弊端,这种方式还是在发送数据包,如果服务器没有直接和客户端连接,那么不会加重服务器的负担。(这一点比超短时间发送心跳包友好)。但是该方式本质上仍然是发送数据包。所以仍然会对直连的 关或者路由设备带来负担
判断有无路由或者路由表时候改变
我们可以通过如下代码来获取默认路由的ip地址
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!