前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >如何判断TCP连接是否可用?

如何判断TCP连接是否可用?

原创
作者头像
泽霖
发布于 2023-11-26 07:14:18
发布于 2023-11-26 07:14:18
3810
举报
文章被收录于专栏:分享技术分享技术

面试遇到一个问题,如何判断一个TCP连接是断开了的,这个断开的意思就是意外中断了,而不是Client或者Server主动断开了。我们知道,TCP维持一个可靠的连接,当没有发起close请求时,默认都是连接的,而Client或者Server如何得知连接的状态就需要利用其他更多方法。

心跳机制

这个问题我觉得大多数人和我一样,第一反应可能就是常见的心跳检测,而心跳机制也有两种技术

应用层心跳机制

最常见的就是应用层HTTP的心跳检测,Server定时向Client发送一个小探测包(可以通过启动一个低级别的线程),根据Client的回复来判断Client是否在线;同样,Client在一段时间内如果没收到心跳包,则认为Server出问题了,连接不可用。

TCP的保活机制(KeepAlive)

应用层的心跳检测有一个问题就是,很多连接的情况下,Server的压力大。

TCP/IP协议内置了KeepAlive功能。不论是Server还是Client,一方开启KeepAlive功能后,就会自动在规定时间内向对方发送心跳包,而另一方在收到心跳包后就会自动回复,以告诉对方我仍然在线。因为开启KeepAlive功能需要消耗额外的宽带和流量,所以TCP协议层默认并不开启KeepAlive功能。而《TCP/CP详解》中也提到这个功能是非必须功能,可能一些协议栈根本没有实现这个功能,原因主要是两个:

  • 对于按流量收费的网络,增加费用开销
  • 不合理的时间设置可能导致短暂的网络波动导致断开了较好的健康连接

目前,部分TCP/IP协议栈实现的KeepAlive时长为2小时

不知道是不是心跳检测只是应用层回答,反正面试官觉得不是自己想要的答案。我也对这个问题来了兴趣,去网上查了一下资料,原来还有其他底层方法。

基于select机制

假设在Server使用多线程方式来处理每个Client的socket连接,Server不主动断开链路,也没有心跳机制来维护连接的状态,Client发送数据的时间也是不一定的。在Client的socket断开后,Server应该能够知道并且释放socket资源。那么Server可以利用select来进行连接是否可用的判断,具体如下:

  1. 设置接收的socket为异步的方式
  2. 使用select()测试一个socket是否可读
  3. 如果select返回值为1,说明socket就绪,使用recv函数读取数据,然后根据请求读取的长度和实际读取的字节数判断,操作如下:
代码语言:txt
AI代码解释
复制
-   对于`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就绪

描述符(socket)就绪(可读可写)的条件

当然,这个问题,被视为圣经之一的《UNIX 网络编程卷1》中的《第6章 I/O复用》早就给我们指明了光明大道。

可读条件

满足下列四个条件中的任何一个时,一个socket准备好读。

  • 该socket接收缓冲区中的数据字节数大于等于socket接收缓存区低水位。对于TCP和UDP而言,缓冲区低水位的值默认为1。那就意味着,默认情况下,只要缓冲区中有数据,那就是可读的。我们可以通过使用SO_RCVLOWAT选项(参见setsockopt函数)来设置该socket的低水位大小。此种描述符就绪(可读)的情况下,当我们使用read/recv等对该socket执行读操作的时候,socket不会阻塞,而是成功返回一个大于0的值(即可读数据的大小)
  • 该连接的读半部关闭(也就是接收了FIN的TCP连接)。对这样的socket的读操作,将不会阻塞,而是返回0(也就是EOF)。
  • 该socket是一个listen的监听socket,并且目前已经完成的连接数不为0。对这样的socket进行accept操作通常不会阻塞。
  • 有一个错误socket待处理。对这样的socket的读操作将不阻塞并返回-1(也就是返回了一个错误),同时把errno设置成确切的错误条件。这些待处理错误(pending error)也可以通过指定SO_ERRORsocket选项调用getsockopt获取并清除。

可写条件

满足下列四个条件中的任何一个时,一个socket准备好写。

  • 该socket发送缓冲区中的可用空间字节数大于等于socket发送缓存区低水位标记时,并且该socket已经成功连接(UDPsocket不需要连接)。对于TCP和UDP而言,这个低水位的值默认为2048,而socket默认的发送缓冲区大小是8k,这就意味着一般一个socket连接成功后,就是处于可写状态的。我们可以通过SO_SNDLOWATsocket选项(参见setsockopt函数)来设置这个低水位。此种情况下,我们设置该socket为非阻塞,对该socket进行写操作(如write,send等),将不阻塞,并返回一个正值(例如由传输层接受的字节数,即发送的数据大小)。
  • 该连接的写半部关闭。对这样的socket的写操作将会产生SIGPIPE信号。所以我们的网络程序基本都要自定义处理SIGPIPE信号。因为SIGPIPE信号的默认处理方式是程序退出。
  • 使用非阻塞的connectsocket已建立连接,或者connect已经以失败告终。即connect有结果了。
  • w有一个错误的socket待处理。对这样的socket的写操作将不阻塞并返回-1(也就是返回了一个错误),同时把errno设置成确切的错误条件。这些待处理的错误也可以通过指定SO_ERRORsocket选项调用getsockopt获取并清除。

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
(六)关于网络编程的一些实用技巧和细节
这些年,接触了形形色色的项目,写了不少网络编程的代码,从windows到linux,跌进了不少坑,由于网络编程涉及很多细节和技巧,一直想写篇文章来总结下这方面的心得与经验,希望对来者有一点帮助,那就善莫大焉了。 本文涉及的平台包括windows和linux,下面开始啦。 一、非阻塞的的connect()函数如何编写 我们知道用connect()函数默认是阻塞的,直到三次握手建立之后,或者实在连不上超时返回,期间程序执行流一直阻塞在那里。那么如何利用connect()函数编写非阻塞的连接代码呢? 无论在win
范蠡
2018/04/04
1.2K0
(六)关于网络编程的一些实用技巧和细节
TCP连接的状态详解以及故障排查
我们通过了解TCP各个状态,可以排除和定位网络或系统故障时大有帮助。(总结网络上的内容)
Java架构师历程
2018/09/26
6.6K0
TCP连接的状态详解以及故障排查
TCP之深入浅出send&recv
接触过网络开发的人,大抵都知道,上层应用使用send函数发送数据,使用recv来接收数据,而send和recv的实现原理又是怎样的呢?
高性能架构探索
2021/09/01
5K1
TCP之深入浅出send&recv
「网络编程」深入浅出Socket网络编程
网络编程又可称为Socket编程。编程分为基于Server端开发与基于Client端开发两部分。基于Server端的编程由四大步骤组成,开发者首先创建Socket,利用bind与listen函数绑定监听地址及相应的端口,最后使用accept函数接受来自监听端的请求。Client端的操作较为简便,开发者在创建Socket后使用connect函数对服务器端进行连接即可实现。
Tinywan
2023/11/14
3650
「网络编程」深入浅出Socket网络编程
TCP连接的状态详解以及故障排查
linux查看tcp的状态命令: 1)、netstat -nat 查看TCP各个状态的数量 2)、lsof -i:port 可以检测到打开套接字的状况 3)、 sar -n SOCK 查看tcp创建的连接数 4)、tcpdump -iany tcp port 9000 对tcp端口为9000的进行抓包 5)、tcpdump dst port 9000 -w dump9000.pcap 对tcp目标端口为9000的进行抓包保存pcap文件wireshark分析。 6)、tcpdump tcp port 9000 -n -X -s 0 -w tcp.cap 对tcp/http目标端口为9000的进行抓包保存pcap文件wireshark分析。
黄规速
2022/04/15
3.6K0
TCP连接的状态详解以及故障排查
关于I/O与并发
由于笔者在之前发布的一文玩转NGINX中提到过I/O复用模型,在此另起一篇文章简述相关技术。
红客突击队
2022/09/30
5940
关于I/O与并发
详解I/O多路转接之select
对大量的描述符进行I/O事件监控—可以告诉进程现在有哪些描述符就绪了,然后进行就可以只针对就绪了的描述符进行响应操作,避免对没有就绪的I/O操作所导致的效率降低和流程阻塞。
海盗船长
2020/08/27
9710
UNPv1第六章:IO复用select&poll
有些进程需要一种预先告知内核的能力,使得内核一旦发现进程指定的一个或多个I/O条件就绪(也就是说输入已准备好被读取,或者描述符已能承受更多的输出),他就通知进程,这个能力称为I/O复用
提莫队长
2019/02/21
5810
linux 网络编程 I/O复用 select,poll ,epoll
http://blog.csdn.net/zs634134578/article/details/19929449
bear_fish
2018/09/20
2.6K0
send,recv,sendto,recvfrom
int send( SOCKET s, const char FAR *buf, int len, int flags );
全栈程序员站长
2022/07/14
1.5K0
基础巩固——长连接 、短连接、心跳机制与断线重连
本文将从长连接和短连接的概念切入,再到长连接与短连接的区别,以及应用场景,引出心跳机制和断线重连,给出代码实现。
Janti
2018/08/01
4.2K0
基础巩固——长连接 、短连接、心跳机制与断线重连
I/O多路复用select/poll/epoll
早期操作系统通常将进程中可创建的线程数限制在一个较低的阈值,大约几百个。因此, 操作系统会提供一些高效的方法来实现多路IO,例如Unix的select和poll。现代操作系统中,线程数已经得到了极大的提升,如NPTL线程软件包可支持数十万的线程。
WindSun
2019/09/09
1.3K0
I/O多路复用select/poll/epoll
经典面试题(三)之网络通信题目集锦
1. TCP/IP协议栈层次结构 2. TCP三次握手需要知道的细节点 3. TCP四次挥手需要知道的细节点(CLOSE_WAIT、TIME_WAIT、MSL) 4. TCP与UDP的区别与适用场景 5. linux常见网络模型详解(select、poll与epoll) 6. epoll_event结构中的epoll_data_t的fd与ptr的使用场景 7. Windows常见的网络模型详解(select、WSAEventSelect、WSAAsyncSelect) 8. Windows上的完成端口模型(
范蠡
2018/04/04
2.6K0
服务器开发中网络数据分析与故障排查经验漫谈
一、 操作系统提供的网络接口 为了能更好的排查网络通信问题,我们需要熟悉操作系统提供的以下网络接口函数,列表如下: 接口函数名称接口函数描述接口函数签名socket创建套接字int socket(int domain, int type, int protocol);connect连接一个服务器地址int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);send发送数据ssiz
范蠡
2018/04/04
1.5K0
服务器开发中网络数据分析与故障排查经验漫谈
网络中进程之间如何通信?
首先考虑一个问题:我们每天打开浏览器浏览网页时,浏览器的进程怎么与web服务器通信的?
用户3946442
2022/04/11
6320
网络中进程之间如何通信?
服务器开发中网络数据分析与故障排查经验谈
为了能更好的排查网络通信问题,我们需要熟悉操作系统提供的以下网络接口函数,列表如下:
范蠡
2020/03/20
1.2K0
linux网络编程系列(八)--优雅关闭以及如何检测对端已经关闭
结构体struct linger如下: struct linger{ int lonoff; int llinger; }; 有以下三种设置情况:
cpp加油站
2021/04/16
3.1K0
linux网络编程系列(八)--优雅关闭以及如何检测对端已经关闭
linux服务器开发三(网络编程) --二
半关闭 当TCP链接中A发送FIN请求关闭,B端回应ACK后(A端进入FIN_WAIT_2状态),B没有立即发送FIN给A时,A方处在半链接状态,此时A可以接收B发送的数据,但是A已不能再向B发送数据。 从程序的角度,可以使用API来控制实现半连接状态。 #include <sys/socket.h> int shutdown(int sockfd, int how); sockfd: 需要关闭的socket的描述符 how: 允许为shutdown操作选择以下几种方式: SHUT
李海彬
2018/03/27
2.5K0
linux服务器开发三(网络编程) --二
Linux下Socket网络编程send和recv使用注意事项
ssize_t send( SOCKET s, const char *buf, size_t len, int flags );
恋喵大鲤鱼
2018/08/03
3.1K0
Linux系统开发: 学习Linux下网络编程
创建网络套接字,用于网络通信使用,类似于文件操作的open函数。该函数在服务器和客户端都会用到。
DS小龙哥
2022/01/27
3.6K0
Linux系统开发: 学习Linux下网络编程
相关推荐
(六)关于网络编程的一些实用技巧和细节
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文