Ⅰ tcp协议依靠什么使释放连接过程得到保障
我忘记了在哪里说过会出现3次挥手的TCP协议的连接是全双工连接,一个TCP连接存在双向的读写通道。 简单说来是 “先关读,后关写”,一共需要四个阶段。以客户机发起关闭连接为例:1.服务器读通道关闭2.客户机写通道关闭3.客户机读通道关闭4.服务器写通道关闭关闭行为是在发起方数据发送完毕之后,给对方发出一个FIN(finish)数据段。直到接收到对方发送的FIN,且对方收到了接收确认ACK之后,双方的数据通信完全结束,过程中每次接收都需要返回确认数据段ACK。详细过程: 第一阶段 客户机发送完数据之后,向服务器发送一个FIN数据段,序列号为i; 1.服务器收到FIN(i)后,返回确认段ACK,序列号为i+1,关闭服务器读通道; 2.客户机收到ACK(i+1)后,关闭客户机写通道; (此时,客户机仍能通过读通道读取服务器的数据,服务器仍能通过写通道写数据) 第二阶段 服务器发送完数据之后,向客户机发送一个FIN数据段,序列号为j; 3.客户机收到FIN(j)后,返回确认段ACK,序列号为j+1,关闭客户机读通道; 4.服务器收到ACK(j+1)后,关闭服务器写通道。这是标准的TCP关闭两个阶段,服务器和客户机都可以发起关闭,完全对称。FIN标识是通过发送最后一块数据时设置的,标准的例子中,服务器还在发送数据,所以要等到发送完的时候,设置FIN(此时可称为TCP连接处于半关闭状态,因为数据仍可从被动关闭一方向主动关闭方传送)。如果在服务器收到FIN(i)时,已经没有数据需要发送,可以在返回ACK(i+1)的时候就设置FIN(j)标识,这样就相当于可以合并第二步和第三步。复制粘贴
Ⅱ TCP协议解析
主要特点:面向连接、面向字节流、全双工通信、通信可靠。
优缺点:
应用场景:要求通信数据可靠时,即 数据要准确无误地传递给对方。如:传输文件:HTTP、HTTPS、FTP等协议;传输邮件:POP、SMTP等协议
ps:首部的前 20 个字节固定,后面有 4n 字节根据需要增加。故 TCP首部最小长度 = 20字节(最大60个字节)。
TCP报头中的源端口号和目的端口号同IP数据报中的源IP与目的IP唯一确定一条TCP连接。
重要字段:
客户端与服务器来回共发送三个TCP报文段来建立运输连接,三个TCP报文段分别为:
(1)客户端A向服务器B发送的TCP请求报段“SYN=1,seq=x”;
(2)服务器B向客户端A发送的TCP确认报文段“SYN=1,ACK=1,seq=y,ack=x+1”;
(3)客户端A向服务器B发送的TCP确认报文段“ACK=1,seq=x+1,ack=y+1”。
ps:在建立TCP连接之前,客户端和服务器都处于关闭状态(CLOSED),直到客户端主动打开连接,服务器才被动打开连接(处于监听状态 = LISTEN),等待客户端的请求。
TCP 协议是一个面向连接的、安全可靠的传输层协议,三次握手的机制是为了保证能建立一个安全可靠的连接。
通过上述三次握手, 双方确认自己与对方的发送与接收是正常的,就建立起一条TCP连接,即可传送应用层数据 。ps:因 TCP提供的是全双工通信,故通信双方的应用进程在任何时候都能发送数据;三次握手期间,任何1次未收到对面的回复,则都会重发。
为什么两次握手不行呢 ?
结论:防止服务器接收了 早已经失效的连接请求报文 ,服务器同意连接,从而一直等待客户端请求, 最终导致形成死锁、浪费资源 。
ps:SYN洪泛攻击:(具体见下文)
为什么不需要四次握手呢 ?
SYN 同步序列编号(Synchronize Sequence Numbers) 是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务器使用 SYN-ACK 应答表示接收到了这个消息,最后客户机再以 ACK确认序号标志消息响应。这样在客户机和服务器之间才能建立起可靠的 TCP 连接,数据才可以在客户机和服务器之间传递。
如何来解决半连接攻击?
如何来解决全连接攻击?
请注意 ,现在 TCP 连接还没有释放掉。必须经过 时间等待计时器 设置的时间 2MSL(MSL:最长报文段寿命)后,客户端才能进入到 CLOSED 状态,然后撤销传输控制块,结束这次 TCP 连接。当然如果服务器一收到 客户端的确认就进入 CLOSED 状态,然后撤销传输控制块。所以在释放连接时,服务器结束 TCP 连接的时间要早于客户端。
TCP是全双工的连接,必须两端同时关闭连接,连接才算真正关闭。 简言之,客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器才会发送 FIN 连接释放报文,对方确认后就完全关闭了TCP连接。
举个例子:A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。
ps:设想这样一个情景: 客户端已主动与服务器建立了 TCP 连接。但后来客户端的主机突然发生故障。 显然,服务器以后就不能再收到客户端发来的数据。因此,应当有措施使服务器不要再白白等待下去。这就需要使用 TCP的保活计时器 。基本原理:
tcp11种状态及变迁其实基本包含在正常的三次握手和四次挥手中,除开CLOSING。
正常的三次握手包括4中状态变迁:
服务器打开监听(LISTEN)->客户端先发起SYN主动连接标识->服务器回复SYN及ACK确认->客户端再确认即三次握手TCP连接成功。这里边涉及四种状态及变迁:
正常的四次握手包含6种tcp状态变迁,如主动发起关闭方为客户端:
客户端发送FIN进入FIN_WAIT1 -> 服务器发送ACK确认并进入CLOSE_WAIT(被动关闭)状态->客户端收到ACK确认后进入FIN_WAIT2状态 -> 服务器再发送FIN进入LAST_ACK状态 -> 客户端收到服务器的FIN后发送ACK确认进入TIME_WAIT状态 -> 服务器收到ACK确认后进入CLOSED状态断开连接 -> 客户端在等待2MSL的时间如果期间没有收到服务器的相关包,则进入CLOSED状态断开连接。
CLOSING状态 :连接断开期间,一般是客户端发送一个FIN,然后服务器回复一个ACK,然后服务器发送完数据后再回复一个FIN,当客户端和服务器同时接受到FIN时,客户端和服务器处于CLOSING状态,也就是此时双方都正在关闭同一个连接。
在进入CLOSING状态后,只要收到了对方对自己发送的FIN的ACK,收到FIN的ACK确认就进入TIME_WAIT状态,因此,如果RTT(Round Trip Time TCP包的往返延时)处在一个可接受的范围内,发出的FIN会很快被ACK从而进入到TIME_WAIT状态,CLOSING状态持续的时间就特别短,因此很难看到这种状态。
我们知道网络层,可以实现两个主机之间的通信。但是这并不具体,因为,真正进行通信的实体是在主机中的进程,是一个主机中的一个进程与另外一个主机中的一个进程在交换数据。IP协议虽然能把数据报文送到目的主机, 但是并没有交付给主机的具体应用进程 。而 端到端的通信才应该是应用进程之间的通信 。
应用场景 :UDP协议比TCP协议的效率更高,TCP协议比UDP协议更加安全可靠。
下面主要对 数据传输出现错误/无应答/堵塞/超时/重复 等问题。
注意:TCP丢包:TCP是基于不可靠的网路实现可靠传输,肯定会存在丢包问题。如果在通信过程中,发现缺少数据或者丢包,那边么 最大的可能性是程序发送过程或者接受过程中出现问题。
总结:为了满足TCP协议不丢包,即保证可靠传输,规定如下:
注意:TCP丢包有三方面的原因,一是网络的传输质量不好,二是安全策略,三是服务器性能瓶颈
先理解2个基础概念:发送窗口、接收窗口
工作原理:
注意点:
关于滑动窗口的知识点:
滑动窗口中的数据类型:
ARQ解决的问题:出现差错时,让发送方重传差错数据:即 出错重传
类型:
流量控制和拥塞控制解决的问题:当接收方来不及接收收到的数据时,可通知发送方降低发送数据的效率:即 速度匹配
流量控制 :
注意:
拥塞控制 :
慢开始与拥塞避免 :
快重传和快恢复 :
补充:流量控制和拥塞控制的区别
什么情况造成TCP粘包和拆包?
解决TCP粘包和拆包的方法:
传输层无法保证数据的可靠传输 ,只能通过应用层来实现了。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层。
最简单的方式是在应用层模仿传输层TCP的可靠性传输。 下面不考虑拥塞处理,可靠UDP的简单设计。
https://www.jianshu.com/p/65605622234b
http://www.open-open.com/lib/view/open1517213611158.html
https://blog.csdn.net/dangzhangjing97/article/details/81008836
https://blog.csdn.net/qq_30108237/article/details/107057946
https://www.jianshu.com/p/6c73a4585eba
Ⅲ 【tcp】心跳检测,保活机制
为什么要心跳检查?
因为目前讨论的数据连接场景,都是无源连接,排除NAT的情况,连接就是存在于src和dest两端OS中的状态机,为什么会要用无源连接呢,有源是连接建立带宽就分配好了,不传有效数据这个带宽也被占用着,这不就浪费了,虚拟信号时代的电话就是有源的。
心跳检查是两端都要做的,不做的那一端一样存在状态不对而不自知的情况。
状态机在两端是有可能不一致的,比如一端认为这条连接已经销毁,另外一端可能认为仍有效。心跳机制的作用之一就是解决这种不一致的情况,类似“校对”的作用。
在浏览器上请求一个需要长时间才得到结果的请求,最后返回超时错误。
你说的情况我们经常遇到,我估计你说的就是客户端自己的超时设置,也就是如果http响应无法在限定时间内完成(比如1秒内),那么客户端应用程序自己会报timeout错误。
这应该不是中间设备做了reset,因为如果是那样,这个中间设备应该会给客户端和服务端都发TCP RST,然后客户端报错会变成ECONNRESET这种(ECONNRESET是linux的网络协议栈报给用户空间程序的报错)。
心跳消息显示push ack是为何呢?
按照TCP协议(参考RFC793)规定,操作系统收到有PSH标志位的报文后,这些报文不会被驻留在接收缓冲区,而是要立即通知用户空间程序来接收。这样对于用户空间程序及时收发处理心跳报文是有利的。
AWS的nlb keep-alive最大是350s,我们的服务端keep-alive是默认配置,客户端使用了连接池,也进行了探测,但是有个骚操作是随机选取池子里面的一个链接进行探测,然后那些超过350s还没探测的链接,再次使用就出问题了。
你说的“随机选取池子里面的一个链接进行探测”,我判断是健康检查,就是业务健康性(类似k8s的readiness probe),而不是keepalive。
如果是这样的话,这个健康检查确实不是为保活而生的,也很难达到保活的目的。建议这样做设置:client idle timeout < LB idle timeout < server idle timeout,这样可以尽可能的保证不出现连接失效的情况。
这里说的“保活时间”,不是心跳包的间隔,而是空闲保活时间,idle timeout。
保活机制:心跳包异常导致应用重启?
https://time.geekbang.org/column/article/482610
如何解决 Keep-Alive 导致 ECONNRESET 的问题
https://zhuanlan.hu.com/p/86953757
解决使用 KeepAlive Agent 遇到的 ECONNRESET
https://zhuanlan.hu.com/p/34147188
Ⅳ tcp连接的断开
TCP的断开就是经过四次挥手:
这是正常的情况,客户端主动tcp连接断开的过程。客户端先是发送一个FIN为一的报文,然后进入FIN_WAIT_1的状态。
服务器收到FIN报文后,发送一个ACK报文,然后进入CLOSED_WAIT状态。
客户端收到服务器的ACK报文进入FIN_WAIT_2状态。
等到服务器觉得他数据处理好了,可以关闭的时候,会发送一个FIN报文,然后进入LAST_ACK。等待最后一个应答。
让客户端收到服务器FIN报文,就进入TIME_WAIT状态了,随后发送最后一个ACK报文,然后close。
客户端再等待2msl后也自己主动关闭。而只有主动关闭的情况下,才会有TIME_WAIT。
那么为什么四次挥手需要四次呢?
三次握手其实就是在第二次把ACK和SYN两个报文合并成一个发,但是断开的过程可能还有一方需要处理下数据,需要延长点时间,等处理好再发FIN,所以就比三次握手多了一次。
这里还有一个问题,为什么需要TIME_WAIT,然后到close需要2msl的时间呢?
先说下什么是MSL,也就是报文的最长生存时间,超过这个时间的报文就要被丢弃掉。tcp是基于ip的,ip上有个生存时间TTL,是ip报文可以经过的最大路由数量,每经过一个路由就减1,减到0,ip报文就丢弃掉,然后通过ICMP通知源主机,我们的ping也算是经过这个。当然msl和ttl还是有区别的,msl是时间,ttl是路由数量,msl也是大于等于ttl的。在linux中,2msl默认是60秒。
前文也说了,只有主动发起断开连接的进程才会有time wait状态。time wait+2msl有两个原因:
1.防止旧连接的数据包
像这个seq 301的包,如果因为网络的原因被延迟了,而没有time wait或者很短,那么连接断开后,又建立新的连接,这个时候这个包到了,可能就导致数据紊乱了。而2msl可以保证两个方向的包在断开前丢弃掉。
2.保证正确的断开连接
2msl的时间也是保证第四个报文的ack可以被被动关闭方接收到。
如图,假设time wait比较短或者没有,当最后的ack报文丢失的时候。客户端已经close了,而服务器一直处于last ack的状态。这样连接就不能正常断开了。而如果有time wait +2msl这个情况就可以避免。假设服务器没有收到最后一个ack报文,服务器会重发FIN等待客户端的ack。
这样就可以保证不会出现一端断开,另外一端没有断开的情况了。
有时候我们在服务器上会看到很多time wait。time wait一般就是服务器主动发起的断开请求才会产生的状态。所以time wait过多,第一个是系统资源会大量消耗,还有是端口如果占的太多,会导致没办法创建新连接。这个时候可以把linux的net.ipv4.tcp_tw_reuse开启,置为1,可以复用time wait超过1秒的连接。
这边再说说tcp的保活机制。也就是怎么长期维持客户端和服务端的连接。
在一个时间段内,如果没有连接等相关活动,tcp的保活机制会定期发探测报文,如果连续几个探测报文就没有回应,就将错误信息报告给系统,系统通知上层应用。
在 Linux 内核可以有对应的参数可以设置保活时间、保活探测的次数、保活探测的时间间隔,以下都为
默认值:
tcp_keepalive_time=7200:表示保活时间是 7200 秒(2⼩时),也就 2 小时内如果没有任何连接
相关的活动,则会启动保活机制
tcp_keepalive_intvl=75:表示每次检测间隔 75 秒;
tcp_keepalive_probes=9:表示检测 9 次无响应,认为对⽅方是不不可达的,从⽽而中断本次的连接。
也就是说在 Linux 系统中,最少需要经过 2 小时 11 分 15 秒才可以发现一个“死亡”连接。
当然这个时间也可以自己配置。
Ⅳ tcp连接plc经常中断
使用TCP/IP协议连接PLC时如果经常出现通讯中断的情况,那么就要做出如下检查,以确定问题所在。
首先,检查网络连接线的好坏,从外观上判断其是否有破损,挤压,或其它损坏现象,若直观查看无法判断最好使用测线仪进行测试,这样更为准确。
其次,检查电脑网卡,系统是否有报警故障,网卡运行是否正常,电脑主机是否有腐蚀,灰尘等环境因素造成的系统运行卡顿。
最后,通过网络测试查看数据发送和接收的速度,并且要查看周围是否有其他网络干扰情况
Ⅵ TCP keepalive 和 http keep-alive 以及心跳保活
HTTP的长连接和短连接本质上是TCP长连接和短连接。
短连接
短连接,顾名思义,与长连接的区别就是,客户端收到服务端的响应后,立刻发送FIN消息,主动释放连接。也有服务端主动断连的情况,凡是在一次消息交互(发请求-收响应)之后立刻断开连接的情况都称为短连接。
长连接/http keep-alive
也叫持久连接,即一个TCP连接服务多次请求,在TCP层握手成功后,不立即断开连接,并在此连接的基础上进行多次消息(包括心跳)交互,直至连接的任意一方(客户端OR服务端)主动断开连接,此过程称为一次完整的长连接。
在HTTP/1.0中得到了初步的支持,客户端在请求header中携带Connection:Keep-Alive,即是在向服务端请求持久连接。如果服务端接受持久连接,则会在响应header中同样携带Connection: Keep-Alive,这样客户端便会继续使用同一个TCP连接发送接下来的若干请求。(Keep-Alive的默认参数是[timout=5, max=100],即一个TCP连接可以服务至多5秒内的100次请求)
HTTP/1.1开始,即使请求header中没有携带Connection: Keep-Alive,传输也会默认以持久连接的方式进行,只有加入"Connection: close "后,才关闭。
http keepalive是客户端浏览器与服务端httpd守护进程协作的结果。
TCP中的KeepAlive
TCP协议的实现中,提供了KeepAlive报文,用来探测连接的对端是否存活。在应用交互的过程中,可能存在以下几种情况:
客户端或服务器意外断电,死机,崩溃,重启;
中间网络已经中断,而客户端与服务器并不知道;
利用保活探测功能,可以探知这种对端的意外情况,从而保证在意外发生时,可以释放半打开的TCP连接。TCP保活报文交互过程如下:
因此,KeepAlive并不适用于检测双方存活的场景,这种场景还得依赖于应用层的心跳。 应用层心跳也具备着更大的灵活性,可以控制检测时机,间隔和处理流程,甚至可以在心跳包上附带额外信息。
应用层心跳是检测连接有效性以及判断双方是否存活的有效方式 。但是心跳过于频繁会带来 耗电和耗流量 的弊病,心跳频率过低则会影响连接检测的实时性。业内关于心跳时间的设置和优化,主要基于如下几个因素:
理想的情况下,客户端应当以略小于NAT超时时间的间隔来发送心跳包。 根据微信团队测试的一些数据,一些常用网络的NAT超时时间如下表所示:
TCP的KeepAlive机制意图在于保活、心跳,检测连接错误
如何快速区分当前连接使用的是长连接还是短连接
1、凡是在一次完整的消息交互(发请求-收响应)之后,立刻断开连接(有一方发送FIN消息)的情况都称为短连接;
2、长连接的一个明显特征是会有心跳消息(也有没有心跳的情况),且一般心跳间隔都在30S或者1MIN左右,用wireshark抓包可以看到有规律的心跳消息交互(可能会存在毫秒级别的误差)。
什么时候用长连接,短连接?
1、需要频繁交互的场景使用长连接,如即时通信工具(微信/QQ,QQ也有UDP),相反则使用短连接,比如普通的web网站,只有当浏览器发起请求时才会建立连接,服务器返回相应后,连接立即断开。
2、维持长连接会有一定的系统开销,用户量少不容易看出系统瓶颈,一旦用户量上去了,就很有可能把服务器资源(内存/CPU/网卡)耗尽,所以使用需谨慎。
keep-alive与TIME_WAIT
使用http keep-alvie,可以减少服务端TIME_WAIT数量(因为由服务端httpd守护进程主动关闭连接)。道理很简单,相较而言,启用keep-alive,建立的tcp连接更少了,自然要被关闭的tcp连接也相应更少了。
短连接和长链接 图:
参考: https://caofengbin.github.io/2018/03/16/dhcp-and-nat/#4-%E5%BD%B1%E5%93%8D%E5%BF%83%E8%B7%B3%E9%A2%91%E7%8E%87%E7%9A%84%E5%85%B3%E9%94%AE%E5%9B%A0%E7%B4%A0
https://blog.csdn.net/hengyunabc/article/details/44310193
http://wingjay.com/2018/12/05/android-arch-long-link/
https://www.levicc.com/2018/06/30/yi-dong-an-wang-luo-you-hua/
能被我参考的都很优秀,哈哈
Ⅶ tcp连接状态详解
unix的哲学是一切皆文件,可以把socket看成是一种特殊的文件,而一些socket函数就是对其进行的操作api(读/写IO、打开、关闭)。我们知道普通文件的打开操作(open)返回一个文件描述字,与之类似,socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。
当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,
sockfd即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。
在将一个地址绑定到socket的时候,需要先将主机字节序转换成为网络字节序,而不要假定主机字节序跟网络字节序一样使用的是Big-Endian。由于这个问题曾引发过不少血案,谨记对主机字节序不要做任何假定,务必将其转化为网络字节序再赋给socket。
这里的主机字节序就是我们平常说的大端和小端模式:不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。引用标准的Big-Endian和Little-Endian的定义如下:
listen函数的第一个参数即为要监听的socket描述字,第二个参数为socket可以接受的排队的最大连接个数。listen函数表示等待客户的连接请求。
connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。
TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就向TCP服务器发送连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数去接收请求,这样连接就建立好了(在connect之后就建立好了三次连接),之后就可以开始进行类似于普通文件的网络I/O操作了。
如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与客户的TCP连接。
accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。
read函数是负责从fd中读取内容.当读成功时,read返回实际所读的字节数,如果返回的值是0表示已经读到文件的结束了,小于0表示出现了错误。如果错误为EINTR说明读是由中断引起的,如果是ECONNREST表示网络连接出了问题。
write函数将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数。失败时返回-1,并设置errno变量。 在网络程序中,当我们向套接字文件描述符写时有俩种可能。1)write的返回值大于0,表示写了部分或者是全部的数据。2)返回的值小于0,此时出现了错误
在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,类似于操作完打开的文件要调用fclose关闭打开的文件。
close一个TCP socket的缺省行为时把该socket标记为已关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数
close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。
我们知道tcp建立连接要进行“三次握手”,即交换三个分组。大致流程如下:
客户端向服务器发送一个SYN J
服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
客户端再想服务器发一个确认ACK K+1
socket中TCP的四次握手释放连接详解
某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。一段时间之后,服务端调用close关闭它的socket。这导致它的TCP也发送一个FIN N;接收到这个FIN的源发送端TCP对它进行确认,这样每个方向上都有一个FIN和ACK。
为什么要三次握手
由于tcp连接是全双工的,存在着双向的读写通道,每个方向都必须单独进行关闭。当一方完成它的数据发送任务后就可以发送一个FIN来终止这个方向的连接。收到FIN只意味着这个方向上没有数据流动,但并不表示在另一个方向上没有读写,所以要双向的读写关闭需要四次握手,
3. time_wait状态如何避免?
首先服务器可以设置SO_REUSEADDR套接字选项来通知内核,如果端口忙,但TCP连接位于TIME_WAIT状态时可以重用端口。在一个非常有用的场景就是,如果你的服务器程序停止后想立即重启,而新的套接字依旧希望使用同一端口,此时SO_REUSEADDR选项就可以避免TIME_WAIT状态。
1.客户端连接服务器的80服务,这时客户端会启用一个本地的端口访问服务器的80,访问完成后关闭此连接,立刻再次访问服务器的
80,这时客户端会启用另一个本地的端口,而不是刚才使用的那个本地端口。原因就是刚才的那个连接还处于TIME_WAIT状态。
2.客户端连接服务器的80服务,这时服务器关闭80端口,立即再次重启80端口的服务,这时可能不会成功启动,原因也是服务器的连
接还处于TIME_WAIT状态。
实战分析:
状态描述:
CLOSED:无连接是活动的或正在进行
LISTEN:服务器在等待进入呼叫
SYN_RECV:一个连接请求已经到达,等待确认
SYN_SENT:应用已经开始,打开一个连接
ESTABLISHED:正常数据传输状态
FIN_WAIT1:应用说它已经完成
FIN_WAIT2:另一边已同意释放
ITMED_WAIT:等待所有分组死掉
CLOSING:两边同时尝试关闭
TIME_WAIT:另一边已初始化一个释放
LAST_ACK:等待所有分组死掉</pre>
命令解释:
如何尽量处理TIMEWAIT过多?
编辑内核文件/etc/sysctl.conf,加入以下内容:
net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_fin_timeout 修改系默认的 TIMEOUT 时间</pre>
然后执行 /sbin/sysctl -p 让参数生效.
/etc/sysctl.conf是一个允许改变正在运行中的Linux系统的接口,它包含一些TCP/IP堆栈和虚拟内存系统的高级选项,修改内核参数永久生效。
简单来说,就是打开系统的TIMEWAIT重用和快速回收。
本文主要讲述了socket的主要api,以及tcp的连接过程和其中各个阶段的连接状态,理解这些是更深入了解tcp的基础!
Ⅷ C#的TCP连接,怎样保持客户端和服务器端一直连接着
只要不执行Socket.Shutdown(),Socket.Close().就会一直连着。当然有时候网络不稳定,可能会失去连接,这时候你使用握手协议,始终监控连接即可。具体做法可以google一下。
Ⅸ 一文带你搞定TCP挥手
TCP断开连接,需要经历四次挥手,通信的双方都可主动断开连接,断开连接通信的双方占用的资源将会被释放。
为什么回收需要四次
原因是客户端在主动发起FIN报文以后仅表示客户端不再主动发送数据了但是还可以接收数据。服务器在响应ACK报文以后,还有可能有数据还在处理且需要发送给客户端,因此当服务器处理完这些数据以后才能发送FIN报文表示同意关闭连接。
因此服务端的ACK和FIN报文需要分开发送,挥手也就变成了4次。
什么是MSL和TTL
TTL是IP头部中的一个字段,是指IP数据报可以经过的最大路由数,每经过一个路由器都需要减1,当TTL值为0时数据报就会被丢弃,同时发送ICMP报文给源主机。TTL的单位是路由跳数。
MSL是报文在网络中存在的最长时间,超过该时间就会被丢弃。
为什么TIME_WAIT需要经历2MSL后才可以变为CLOSED
网络中存在的发送方数据包,首先需要发送给服务端,服务端在处理完以后又会将相应发送给客户端,所以总共需要2个倍的时间。
2MSL的时间是从客户端接收到FIN报文并且发送ACK报文时开始的。如果此时ACK报文没有被服务端接收到触发了服务端的超时重传,客户端又再次收到了FIN报文,那么2MSL将重新开始计时。
LINUX中默认一个MSL是30s,也就是说TIME_WAIT的时间是60s。
为什么需要TIME_WAIT状态
主动发起连接中断的一方需要有TIME_WAIT状态,主要是以下原因:
防止旧连接的数据包被收到
假设没有TIME_WAIT状态,如果有相同的端口的TCP连接被服用后,上图中被延迟SEQ=301的数据包抵达了客户端,客户端是有可能正常接收该报文的,此时就会产生数据错乱现象。
但通过2MSL的等待时间,通信双方的数据包都可以在网络中消失,新的数据包一定是新连接的。
保证连接正确关闭
通过等待2MSL的时间确保最后一次ACK报文被被动断开连接的一方收到,从而正常关闭。
上图如果服务端没有收到最后一个ACK报文会处于LAST_ACK状态,如果此时客户端发起了一个新的SYN报文请求建立连接,服务端会发送RST报文给客户端,连接建立失败。
但是通过等待2MSL的时间会解决上述问题,因为假设服务端没有收到最后一次ACK报文,会触发超时重传重新发送FIN报文并等待新的ACK报文。客户端在收到新的FIN报文时会重新发送ACK报文并刷新2MSL的计时,最终能够保证服务端的连接能够正常关闭。
TIME_WAIT过多的弊端
服务器如果有TIME_WAIT状态的连接,说明TCP连接的断开是由服务端发起的,此时如果TIME_WAIT的连接过多,将会出现以下问题:
TIME_WAIT的优化主要有以下几种方式,每种方式都有利有弊:
打开net.ipv4.tcp_tw_reuse和net.ipv4.tcp_timestamps选项
参数开启以后,可以复用处于TIME_WAIT的Socket给新的连接使用。
tcp_tw_reuse的功能只能用于连接发起方,开启该参数以后,在调用connect函数时,内核会随机找一个time_wait超过1s的连接给新的连接复用。
net.ipv4.tcp_timestamp默认开启,表示打开对TCP时间戳的支持。时间戳字段存储在TCP头部的选项字段中,用于记录TCP发送方的时间戳和从对端接收到的最新时间戳。
net.ipv4.tcp_max_tw_buckets
当系统中的TIME_WAIT的连接数超过该项的值时,系统那个会将后面TIME_WAIT的连接重置,不推荐使用。
程序使用SO_LINGER
通过设置Sokcet的一些选项,来影响close方法的一些行为。
如果SO_LINGER中的onoff为非0,并且linger为0,调用close方法以后会立即发送一个RST报文给对方,TCP连接会直接跳过四次握手关闭。也过于暴力不推荐。
在某个时间段内,如果TCP连接上无任何活动,TCP保活机制开始生效,每隔一段时间就会发送一个探测报文,如果连续几个探测报文都没有收到响应,则认为TCP连接已死,系统内核会将错误信息通知给应用程序。
上述三个都是Linux中的默认值,也就是说Linux操作系统中至少经过2小时11分15秒才可以发现一个死亡连接。
Java中的ServerSokcet的初始化方法中有一个backlog参数,该参数在Linux2.2以前代表SYN队列大小,但是在Linux 2.2以后就是全连接队列的大小(accept队列的大小)。
Socket的一些连接操作对应的tcp连接步骤
close方法对应的TCP四次挥手