面试遇到一个问题,如何判断一个TCP连接是断开了的,这个断开的意思就是意外中断了,而不是Client或者Server主动断开了。我们知道,TCP维持一个可靠的连接,当没有发起close请求时,默认都是连接的,而Client或者Server如何得知连接的状态就需要利用其他更多方法。
这个问题我觉得大多数人和我一样,第一反应可能就是常见的心跳检测,而心跳机制也有两种技术
最常见的就是应用层HTTP的心跳检测,Server定时向Client发送一个小探测包(可以通过启动一个低级别的线程),根据Client的回复来判断Client是否在线;同样,Client在一段时间内如果没收到心跳包,则认为Server出问题了,连接不可用。
应用层的心跳检测有一个问题就是,很多连接的情况下,Server的压力大。
TCP/IP协议内置了KeepAlive功能。不论是Server还是Client,一方开启KeepAlive功能后,就会自动在规定时间内向对方发送心跳包,而另一方在收到心跳包后就会自动回复,以告诉对方我仍然在线。因为开启KeepAlive功能需要消耗额外的宽带和流量,所以TCP协议层默认并不开启KeepAlive功能。而《TCP/CP详解》中也提到这个功能是非必须功能,可能一些协议栈根本没有实现这个功能,原因主要是两个:
目前,部分TCP/IP协议栈实现的KeepAlive时长为2小时
不知道是不是心跳检测只是应用层回答,反正面试官觉得不是自己想要的答案。我也对这个问题来了兴趣,去网上查了一下资料,原来还有其他底层方法。
假设在Server使用多线程方式来处理每个Client的socket连接,Server不主动断开链路,也没有心跳机制来维护连接的状态,Client发送数据的时间也是不一定的。在Client的socket断开后,Server应该能够知道并且释放socket资源。那么Server可以利用select来进行连接是否可用的判断,具体如下:
recv
函数读取数据,然后根据请求读取的长度和实际读取的字节数判断,操作如下:- 对于`recv`函数来说,传入socket描述符,缓冲区指针,**请求读取的长度**,**`flag`设置为阻塞型 `MSG_WAITALL`**
- 根据这个`flag`,内核在没有读取到足够请求长度的数据之前会一直阻塞,除非发生以下这些情况,会返回当前已经读取的字节数: **(1)收到终止信号;(2)连接终止;(3)套接字发生错误。**
- `recv`的返回值为0,说明另一方是正常关闭的。
- `recv`的返回值小于0(也就是-1), 说明客户端的连接可能已经断开,但是还需要判断`errno`是否为`EINTR`(中断信号),因为如果是因为接收到中断信号,那么`recv`函数也会返回,而此时socket可能还是正常的。也就是说当recv返回-1时,可能是socket出现问题,也可能是中断信号,需要进一步判断。
利用recv
阻塞的读取返回也能够判断连接是否可用,而基于这套机制,最核心的问题就是:IO复用的过程中如何判断一个socket可读,或者说就绪,也就是select什么时候会返回socket就绪
当然,这个问题,被视为圣经之一的《UNIX 网络编程卷1》中的《第6章 I/O复用》早就给我们指明了光明大道。
满足下列四个条件中的任何一个时,一个socket准备好读。
满足下列四个条件中的任何一个时,一个socket准备好写。
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有