TCP链接简介
当网络通讯时采用TCP协议时,在真正的读写操做以前,server与client之间必须创建一个链接,
当读写操做完成后,双方再也不须要这个链接时它们能够释放这个链接,
链接的创建是须要三次握手的,而释放则须要4次握手,
因此说每一个链接的创建都是须要资源消耗和时间消耗的
经典的三次握手示意图:
python
经典的四次握手关闭图:linux
1、长链接与短链接
长链接: 指在一个TCP链接上能够连续发送多个数据包,
在TCP链接保持期间,若是没有数据包发送,须要双方发检测包以维持此链接;
通常须要本身作在线维持。
短链接: 指通讯双方有数据交互时,就创建一个TCP链接,数据发送完成后,则断开此TCP链接;
通常银行都使用短链接。
它的优势是:管理起来比较简单,存在的链接都是有用的链接,不须要额外的控制手段
好比http的,只是链接、请求、关闭,过程时间较短,服务器如果一段时间内没有收到请求便可关闭链接。
其实长链接是相对于一般的短链接而说的,也就是长时间保持客户端与服务端的链接状态。
长链接与短链接的操做过程
一般的短链接操做步骤是:
链接→数据传输→关闭链接;
而长链接一般就是:
链接→数据传输→保持链接(心跳)→数据传输→保持链接(心跳)→……→关闭链接;
这就要求长链接在没有数据通讯时,定时发送数据包(心跳),以维持链接状态,
短链接在没有数据传输时直接关闭就好了
何时用长链接,短链接?
长链接多用于操做频繁,点对点的通信,并且链接数不能太多状况。
每一个TCP链接都须要三步握手,这须要时间,若是每一个操做都是先链接,再操做的话那么处理速度会下降不少,
因此每一个操做完后都不断开,下次次处理时直接发送数据包就OK了,不用创建TCP链接。
例如:数据库的链接用长链接,
若是用短链接频繁的通讯会形成socket错误,并且频繁的socket 建立也是对资源的浪费。
2、发送接收方式
一、异步
报文发送和接收是分开的,相互独立的,互不影响。这种方式又分两种状况:
(1)异步双工:接收和发送在同一个程序中,由两个不一样的子进程分别负责发送和接收
(2)异步单工:接收和发送是用两个不一样的程序来完成。
二、同步
报文发送和接收是同步进行,既报文发送后等待接收返回报文。
同步方式通常须要考虑超时问题,即报文发出去后不能无限等待,须要设定超时时间,
超过该时间发送方再也不等待读返回报文,直接通知超时返回。
在长链接中通常是没有条件可以判断读写何时结束,因此必需要加长度报文头。
读函数先是读取报文头的长度,再根据这个长度去读相应长度的报文。
三. 单工、半双工和全双工
根据通讯双方的分工和信号传输方向可将通讯分为三种方式:
单工、
半双工、
全双工。
在计算机网络中主要采用双工方式,其中:
局域网采用半双工方式,
城域网和广域网采用全双年方式。
1. 单工(Simplex)方式:
通讯双方设备中发送器与接收器分工明确,只能在由发送器向接收器的单一固定方向上传送数据。
采用单工通讯的典型发送设备如早期计算机的读卡器,典型的接收设备如打印机。
2. 半双工(Half Duplex)方式:
通讯双方设备既是发送器,也是接收器,两台设备能够相互传送数据,但某一时刻则只能向一个方向传送数据。
例如,步话机是半双工设备,由于在一个时刻只能有一方说话。
3. 全双工(Full Duplex)方式:
通讯双方设备既是发送器,也是接收器,两台设备能够同时在两个方向上传送数据。
例如,电话是全双工设备,由于双方可同时说话。
而像WEB网站的http服务通常都用短连接,由于长链接对于服务端来讲会耗费必定的资源,
而像WEB网站这么频繁的成千上万甚至上亿客户端的链接用短链接会更省一些资源,
若是用长链接,并且同时有成千上万的用户,若是每一个用户都占用一个链接的话,那可想而知吧。
因此并发量大,但每一个用户无需频繁操做状况下需用短连好。
总之,长链接和短链接的选择要视状况而定。git
心跳包机制github
跳包之因此叫心跳包是由于:它像心跳同样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长链接,至于这个包的内容,是没有什么特别规定的,不过通常都是很小的包,或者只包含包头的一个空包。
在TCP的机制里面,自己是存在有心跳包的机制的,也就是TCP的选项:SO_KEEPALIVE。系统默认是设置的2小时的心跳频率。可是它检查不到机器断电、网线拔出、防火墙这些断线。并且逻辑层处理断线可能也不是那么好处理。通常,若是只是用于保活仍是能够的。
心跳包通常来讲都是在逻辑层发送空的echo包来实现的。下一个定时器,在必定时间间隔下发送一个空包给客户端,而后客户端反馈一个一样的空包回来,服务器若是在必定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。
其实,要断定掉线,只须要send或者recv一下,若是结果为零,则为掉线。可是,在长链接下,有可能很长一段时间都没有数据往来。理论上说,这个链接是一直保持链接的,可是实际状况中,若是中间节点出现什么故障是难以知道的。更要命的是,有的节点(防火墙)会自动把必定时间以内没有数据交互的链接给断掉。在这个时候,就须要咱们的心跳包了,用于维持长链接,保活。
在获知了断线以后,服务器逻辑可能须要作一些事情,好比断线后的数据清理呀,从新链接呀……固然,这个天然是要由逻辑层根据需求去作了。
总的来讲,心跳包主要也就是用于长链接的保活和断线处理。通常的应用下,断定时间在30-40秒比较不错。若是实在要求高,那就在6-9秒。数据库
心跳检测步骤:
1 客户端每隔一个时间间隔发生一个探测包给服务器
2 客户端发包时启动一个超时定时器
3 服务器端接收到检测包,应该回应一个包
4 若是客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
5 若是客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了编程
用man命令,能够查看linux的tcp的参数:bash
man 7 tcp
其中keep-alive相关的参数有三个:服务器
tcp_keepalive_intvl (integer; default: 75; since Linux 2.4) The number of seconds between TCP keep-alive probes. tcp_keepalive_probes (integer; default: 9; since Linux 2.2) The maximum number of TCP keep-alive probes to send before giving up and killing the connection if no response is obtained from the other end. tcp_keepalive_time (integer; default: 7200; since Linux 2.2) The number of seconds a connection needs to be idle before TCP begins sending out keep-alive probes. Keep- alives are sent only when the SO_KEEPALIVE socket option is enabled. The default value is 7200 seconds (2 hours). An idle connection is terminated after approximately an additional 11 minutes (9 probes an interval of 75 seconds apart) when keep-alive is enabled.
这些的默认配置值在/proc/sys/net/ipv4 目录下能够找到。微信
能够直接用cat来查看文件的内容,就能够知道配置的值了。
也能够经过sysctl命令来查看和修改:网络
# 查询 cat /proc/sys/net/ipv4/tcp_keepalive_time sysctl net.ipv4.tcp_keepalive_time #修改 sysctl net.ipv4.tcp_keepalive_time=3600
上面三个是系统级的配置,在编程时有三个参数对应,能够覆盖掉系统的配置:
TCP_KEEPCNT 覆盖 tcp_keepalive_probes,默认9(次) TCP_KEEPIDLE 覆盖 tcp_keepalive_time,默认7200(秒) TCP_KEEPINTVL 覆盖 tcp_keepalive_intvl,默认75(秒) ``` ## tcp keep-alive的本质 ###TCP keep-alive probe 上面了解了tcp keep-alive的一些参数,下面来探究下其本质。 在远程机器192.168.66.123上,用nc启动一个TCP服务器: ```bash nc -l 9999
在本地机器上,用python建立一个socket去链接,而且用wireshark抓包分析
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) s.setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, 20) s.setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, 1) s.connect(('192.168.66.123', 9999))
上面的程序,设置了TCP_KEEPIDLE为20,TCP_KEEPINTVL为1,系统默认的tcp_keepalive_probes是9。
当网络正常,不作干扰时,wireshark抓包的数据是这样的(注意看第二列Time):
能够看到,当3次握手完成以后,每隔20秒以后66.120发送了一个TCP Keep-Alive的数据包,而后66.123回应了一个TCP Keep-Alive ACK包。这个就是TCP keep-alive的实现原理了。
当发送了第一个TCP Keep-Alive包以后,拨掉192.168.66.123的网线,而后数据包是这样子的:
能够看到,当远程服务器192.168.66.123网络失去链接以后,本地机器(192.168.66.120)每隔一秒重发了9次tcp keep-alive probe,最终认为这个TCP链接已经失效,发了一个RST包给192.168.66.123。
在本地机器上,用python建立一个socket去链接,而且用wireshark抓包分析 ```python import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) s.setsockopt(socket.SOL_TCP, socket.TCP_KEEPIDLE, 20) s.setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, 1) s.connect(('192.168.66.123', 9999))
上面的程序,设置了TCP_KEEPIDLE为20,TCP_KEEPINTVL为1,系统默认的tcp_keepalive_probes是9。
当网络正常,不作干扰时,wireshark抓包的数据是这样的(注意看第二列Time):
能够看到,当3次握手完成以后,每隔20秒以后66.120发送了一个TCP Keep-Alive的数据包,而后66.123回应了一个TCP Keep-Alive ACK包。这个就是TCP keep-alive的实现原理了。
当发送了第一个TCP Keep-Alive包以后,拨掉192.168.66.123的网线,而后数据包是这样子的:
能够看到,当远程服务器192.168.66.123网络失去链接以后,本地机器(192.168.66.120)每隔一秒重发了9次tcp keep-alive probe,最终认为这个TCP链接已经失效,发了一个RST包给192.168.66.123。
默认是7200秒,也就是2个小时。
socks协议只管转发TCP层具体的数据包,而不会转发TCP协议内的实现细节的包(也作不到),参考socks_proxy。
因此,一个应用若是使用了socks代理,那么tcp keep-alive机制就失效了,因此应用要本身有心跳包。
socks proxy只是一个例子,真实的网络很复杂,可能会有各类缘由让tcp keep-alive失效。
前两年,微信信令事件很火,搜索下“微信 信令”或者“移动网络 信令”能够查到不少相关文章。
这里附上一个连接:微信的大规模使用真的会过多占用信令,影响通信稳定吗?
TCP keep-alive是经过在空闲时发送TCP Keep-Alive数据包,而后对方回应TCP Keep-Alive ACK来实现的。
为何须要heart beat/心跳包?由于tcp keep-alive不能知足人们的实时性的要求,就是这么简单。