目录
通信方式
连接方式
1.长连接
1.1服务器中的长连接
1.2 长连接的维护
1.3 长连接保活/心跳
1.4 连接的保活:KeepAlive
2.短连接
总结
理论篇:
(一)深入浅出TCPIP之理解TCP报文格式和交互流程
(二)深入浅出TCPIP之再识TCP,理解TCP三次握手(上)
(三)深入浅出TCPIP之再识TCP,理解TCP四次挥手(上)
(四)深入浅出TCPIP之TCP三次握手和四次挥手(下)的抓包分析
(五)深入浅出TCPIP之TCP流量控制
(六)深入浅出TCPIP之TCP拥塞控制
(七)深入浅出TCPIP之深入浅出TCPIP之TCP重传机制
(八)深入浅出TCPIP之TCP长连接与短连接详解
(九)深入浅出TCPIP之网络同步异步
(十)深入浅出TCPIP之网络阻塞和非阻塞
(十一)深入浅出TCPIP之TCP粘包问题
(十二)深入浅出TCPIP之Nagle算法
(十三) 深入浅出TCPIP之TCP套接字参数
(十四)深入浅出TCPIP之初识UDP理解报文格式和交互流程
(十五)非常全面的TCPIP面试宝典-进入大厂必备总结
(十六)深入浅出TCPIP之Hello CDN
....
(二十)深入浅出TCPIP之epoll的一些思考
实践篇:
深入浅出TCPIP之实战篇—用c++开发一个http服务器(二十一)
其他实践篇+游戏开发中的网络问题疑难杂症解读 正在完善。。。
刚接触TCP/IP通信设计的人根据范例可以很快编出一个通信程序,据此一些人可能会认为TCP/IP编程很简单。其实不然,TCP/IP编程具有较为丰富的内容。其编程的丰富性主要体现在通信方式和报文格式的多样性上。
主要有以下三大类:
1.一个Client方连接一个Server方,或称点对点(peer to peer):
2.多个Client方连接一个Server方,这也是通常的并发服务器方式。
3.一个Client方连接多个Server方,这种方式很少见,主要用于一个客户向多个服务器发送请求情况。
长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差。
Client方与Server方先建立通讯连接,连接建立后不断开, 然后再进行报文发送和接收。
连接->传输数据->保持连接 -> 传输数据-> 。。。 ->关闭连接。
我们在追求性能的时候,会选择使用长连接,这里我有一个服务器程序, 负责监听本地6032,多个 client 负责循环发送请求。执行 lsof -i:6032
命令可以查看端口的相关使用情况:
*:6032(LISTEN)
说明了服务器正在监听本地的6032端口,处理发送到本地 6032 端口的请求
前两条信息说明请求的发送情况,验证了 TCP 是一个双向的通信过程,由于我在本地启动了2个客户端模拟器,所以你能够看到是本地的 21095 ,21598端口与6032端口在通信。我们并没有手动设置 53078 这个客户端端口,他是随机的,但也阐释了一个道理:即使是发送请求的一方,也需要占用一个端口。
稍微说一下 FD 这个参数(第四列),他代表了文件句柄,每新增一条连接都会占用新的文件句柄,如果你在使用 TCP 通信的过程中出现了 open too many files
的异常,那就应该检查一下,你是不是创建了太多的连接,而没有关闭。细心的读者也会联想到长连接的另一个好处,那就是会占用较少的文件句柄。
因为客户端请求的服务可能分布在多个服务器上,客户端端自然需要跟对端创建多条长连接,使用长连接,我们遇到的第一个问题就是要如何维护长连接。实际上我们客户端和服务端都使用 ip:port
维护了端对端的长连接,Channel 或者Session便是对连接的抽象。 我会在后续的涉及到网络编程的地方来用代码来说明举例。
为什么需要连接的保活心跳?当双方已经建立了连接,但因为网络问题,链路不通,这样长连接就不能使用了。需要明确的一点是,通过 netstat,lsof 等指令查看到连接的状态处于 ESTABLISHED
状态并不是一件非常靠谱的事,因为连接可能已死,但没有被系统感知到,更不用提假死这种疑难杂症了。如果保证长连接可用是一件技术活。通常我们会用客户端和服务器之间保持心跳连接,也就是说客户端和服务器之间定时发送心跳协议,如果服务器在一定时间范围内收到来自客户端的心跳协议并返回给客户端,那么客户端认为服务器是“活”的,否则认为此连接可能是死连接,主动断开和服务器之间的连接。
首先想到的是 TCP 中的 KeepAlive 机制。KeepAlive 并不是 TCP 协议的一部分,但是大多数操作系统都实现了这个机制。KeepAlive 机制开启后,在一定时间内(一般时间为 7200s,参数 tcp_keepalive_time
)在链路上没有数据传送的情况下,TCP 层将发送相应的KeepAlive探针以确定连接可用性,探测失败后重试 10(参数 tcp_keepalive_probes
)次,每次间隔时间 75s(参数 tcp_keepalive_intvl
),所有探测失败后,才认为当前连接已经不可用。
在 Netty 中开启 KeepAlive:
bootstrap.option(ChannelOption.TCP_NODELAY, true)
Linux 操作系统中设置 KeepAlive 相关参数,修改 /etc/sysctl.conf
文件:
net.ipv4.tcp_keepalive_time=90net.ipv4.tcp_keepalive_intvl=15net.ipv4.tcp_keepalive_probes=2
KeepAlive 机制是在网络层面保证了连接的可用性,但站在应用框架层面我们认为这还不够。主要体现在两个方面:
KeepAlive 的开关是在应用层开启的,但是具体参数(如重试测试,重试间隔时间)的设置却是操作系统级别的,位于操作系统的 /etc/sysctl.conf
配置中,这对于应用来说不够灵活。
KeepAlive 的保活机制只在链路空闲的情况下才会起到作用,假如此时有数据发送,且物理链路已经不通,操作系统这边的链路状态还是 ESTABLISHED,这时会发生什么?自然会走 TCP 重传机制,要知道默认的 TCP 超时重传,指数退避算法也是一个相当长的过程。
KeepAlive 本身是面向网络的,并不是面向于应用的,当连接不可用时,可能是由于应用本身 GC 问题,系统 load 高等情况,但网络仍然是通的,此时,应用已经失去了活性,所以连接自然应该认为是不可用的。
看来,应用层面的连接保活还是必须要做的。
Client方与Server每进行一次报文收发交易时才进行通讯连接,交易完毕后立即断开连接。
过程:
连接->传输数据->关闭连接
HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。
也可以这样说:短连接是指SOCKET连接后发送后接收完数据后马上断开连接。
之前的文章里我已经说过一个TCP的连接建立和关闭,通常是需要三次握手和四次挥手的。所以长连接和短连接的优缺点也就突显了出来。
连接 | 优点 | 缺点 |
---|---|---|
长连接 | 传输速度快,server可以主动发送数据给client | 保持的连接会占用和多系统资源, 后台设计相对要复杂 |
短连接 | 不需要占用系统的太多的资源使得server可以处理更多client的connect | 发送小数据划不来,比较耗时。server无法主动发送数据到client |