动漫风格网站,网页qq官网,网站建设公司四川,珠海企业官网设计制作三次握手
三次握手过程 TCP服务器进程先创建传输控制块TCB#xff0c;时刻准备接受客户进程的连接请求#xff0c;此时服务器就进入了LISTEN#xff08;监听#xff09;状态#xff1b;TCP客户进程也是先创建传输控制块TCB#xff0c;然后向服务器发出连接请求报文…三次握手
三次握手过程 TCP服务器进程先创建传输控制块TCB时刻准备接受客户进程的连接请求此时服务器就进入了LISTEN监听状态TCP客户进程也是先创建传输控制块TCB然后向服务器发出连接请求报文此时是报文首部中的同步位SYN1同时选择一个初始序列号 seqx 此时TCP客户端进程进入了 SYN-SENT同步已发送状态状态。TCP规定SYN报文段SYN1的报文段不能携带数据但需要消耗掉一个序号。TCP服务器收到请求报文后如果同意连接则发出确认报文。确认报文中应该 ACK1SYN1确认号是ackx1同时也要为自己初始化一个序列号 seqy此时TCP服务器进程进入了 SYN-RCVD同步收到状态。这个报文也不能携带数据但是同样要消耗一个序号。TCP客户进程收到确认后还要向服务器给出确认。确认报文的ACK1acky1自己的序列号seqx1此时TCP连接建立客户端进入ESTABLISHED已建立连接状态。TCP规定ACK报文段可以携带数据但是如果不携带数据则不消耗序号。当服务器收到客户端的确认后也进入ESTABLISHED状态此后双方就可以开始通信了。
三次握手期间调用的系统调用 当客户端调用connect时触发了连接请求向服务器发送了SYN J包这时connect进入阻塞状态服务器监听到连接请求即收到SYN J包调用accept函数接收请求向客户端发送SYN K ACK J1这时accept进入阻塞状态客户端收到服务器的SYN K ACK J1之后这时connect返回并对SYN K进行确认服务器收到ACK K1时accept返回至此三次握手完毕连接建立。
为什么建立连接使用三次握手而不是两次握手
主要防止已经失效的连接请求报文突然又传送到了服务器从而产生错误。
如果使用的是两次握手建立连接假设有这样一种场景客户端发送了第一个请求连接并且没有丢失只是因为在网络结点中滞留的时间太长了由于TCP的客户端迟迟没有收到确认报文以为服务器没有收到此时重新向服务器发送这条报文此后客户端和服务器经过两次握手完成连接传输数据然后关闭连接。此时此前滞留的那一次请求连接网络通畅了到达了服务器这个报文本该是失效的但是两次握手的机制将会让客户端和服务器再次建立连接这将导致不必要的错误和资源的浪费。如果采用的是三次握手就算是那一次失效的报文传送过来了服务端接受到了那条失效报文并且回复了确认报文但是客户端不会再次发出确认。由于服务器收不到确认就知道客户端并没有请求连接。
TCP进行通信时的初始序列号为什么是随机的
考虑场景B是服务器A是一个合法的客户端C假冒A比如模拟IP等和B进行通信。
C假冒AB接受后把ACK会直接发给A。由于A没有发送过seqISN _C的请求当A收到ISN_C的ack后直接发送reset 给B最终关闭了链接。如下图所示
假如初始序列号不是随机的而是可以推测的那么C就可以拿到ISN_B然后模拟一个ACK过去B最终会建立链接C开始传递数据这就会产生非常严重的安全问题比如SYN泛洪所以ISN随机是必须的。 如果没有资源限制一个服务器最多可以承载多少连接
一个TCP连接由一个四元组所确定源IP源端口目的IP目的端口。任意一个元素改变都代表一个新的连接以Nginx为例它的端口是固定使用80。另外服务器的IP也是固定的那么理论上最多可以建立 2 ^ 32 (ip数 × 2 ^ 16 (端口数) 个连接。
实际上能建立的连接远小于这个数字。我们每打开一个连接都对应一个文件描述符fd而linux系统在多个位置都限制了可打开的文件描述符的数量。
Address in use错误信息
在实践中我们可能会经常碰到一个问题当 TCP 服务进程重启之后总是碰到“Address in use”的报错信息TCP 服务进程不能很快地重启而是要过一会才能重启成功。这是为什么呢
当我们重启 TCP 服务进程的时候意味着通过服务器端发起了关闭连接操作服务端会出现 TIME_WAIT 状态的连接TIME_WAIT 状态的连接使用的 IPPORT 仍然被认为是一个有效的 IPPORT 组合相同机器上不能够在该 IPPORT 组合上进行绑定那么执行 bind() 函数的时候就会返回了 Address already in use 的错误。而等 TIME_WAIT 状态的连接结束后重启 TCP 服务进程就能成功。
重启 TCP 服务进程时如何避免“Address in use”的报错信息
我们可以在调用 bind 前对 socket 设置 SO_REUSEADDR 属性可以解决这个问题。SO_REUSEADDR 作用是如果当前启动进程绑定的 IPPORT 与处于TIME_WAIT 状态的连接占用的 IPPORT 存在冲突但是新启动的进程使用了 SO_REUSEADDR 选项那么该进程就可以绑定成功。
四次挥手
四次挥手过程 客户端进程发出连接释放报文并且停止发送数据。释放数据报文首部FIN1其序列号为sequ等于前面已经传送过来的数据的最后一个字节的序号加1此时客户端进入FIN-WAIT-1终止等待1状态。 TCP规定FIN报文段即使不携带数据也要消耗一个序号。服务器收到连接释放报文发出确认报文ACK1acku1并且带上自己的序列号seqv此时服务端就进入了CLOSE-WAIT关闭等待状态。TCP服务器通知高层的应用进程客户端向服务器的方向就释放了这时候处于半关闭状态即客户端已经没有数据要发送了但是服务器若发送数据客户端依然要接受。这个状态还要持续一段时间也就是整个CLOSE-WAIT状态持续的时间。客户端收到服务器的确认请求后此时客户端就进入FIN-WAIT-2终止等待2状态等待服务器发送连接释放报文在这之前还需要接受服务器发送的最后的数据。服务器将最后的数据发送完毕后就向客户端发送连接释放报文FIN1acku1由于在半关闭状态服务器很可能又发送了一些数据假定此时的序列号为seqw此时服务器就进入了LAST-ACK最后确认状态等待客户端的确认。客户端收到服务器的连接释放报文后必须发出确认ACK1ackw1而自己的序列号是sequ1此时客户端就进入了TIME-WAIT时间等待状态。注意此时TCP连接还没有释放必须经过2∗MSL最长报文段寿命的时间后当客户端撤销相应的TCP后才进入CLOSED状态。服务器只要收到了客户端发出的确认立即进入CLOSED状态。同样撤销TCP后就结束了这次的TCP连接。可以看到服务器结束TCP连接的时间要比客户端早一些。
为什么建立连接协议是三次握手而关闭连接却是四次握手
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建立连接请求后它可以把ACK和SYNACK起应答作用而SYN起同步作用放在一个报文里来发送。但关闭连接时当收到对方的FIN报文通知时它仅仅表示对方没有数据发送给你了但未必你所有的数据都全部发送给对方了所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后再发送FIN报文给对方来表示你同意现在可以关闭连接了所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
为什么关闭连接是四次挥手而不是三次挥手
因为TCP是全双工通信的。被动关闭方发送ACK报文只是确认主动关闭方发来的FIN报文但并不代表自身的数据已经传输完毕。即主动关闭方断开连接的时候只是主动关闭方的数据已传输完毕而被动关闭方或许还有数据未发送所以要等到被动关闭方要发送的数据全部传输完成后被动关闭方发送FIN报文才能可靠的关闭连接。
三次挥手只能将被动关闭方发出的ACK报文和FIN报文合并但这样合并是有问题的。会造成主动关闭方的FIN报文长时间未得到响应而进行超时重传等等造成了不必要的资源浪费甚至更意想不到的问题。
挥手报文丢失会发生什么
第一次挥手报文丢失当客户端调用close函数后就会向服务端发送FIN报文试图与服务端断开联系此时客户端进入FIN_WAIT_1状态。如果客户端一直收不到ack应答报文的话就会触发超时重传机制最大重传次数由tcp_orphan_retries参数决定。当超过指定次数时就不再发送报文直接进入close状态
第二次挥手报文丢失当接受到客户端的FIN报文就会先回应一个ack报文此时服务端进入close_wait状态。当ack报文丢失时ack是不会重传的。服务端的ack报文丢失了客户端就会触发超时重传直到收到ack报文或则到达超时重传次数。
第三次挥手报文丢失类似于第一次挥手报文丢失。
第四次挥手报文丢失类似于第二次挥手报文丢失。
TIME_WAIT状态详解
TIME_WAIT的意义关闭TCP连接为什么要等于2MSL
MSL表示最长报文段寿命。
1保证客户端发送的最后一个ACK报文能够到达服务器因为这个ACK报文可能丢失站在服务器的角度看来我已经发送了FINACK报文请求断开了客户端还没有给我回应应该是我发送的请求断开报文它没有收到由于RTO重传超时时间远小于MSL因此服务器会触发超时重传而客户端就能在这个2MSL时间段内收到这个重传的报文接着给出回应报文并且会重启2MSL计时器。
2防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后在这个2MSL时间中就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。
为什么不是一个MSL? 如果最后一个ACK包丢了,检测到丢包时用了一个MSL,这时主动关闭端就关闭了,对方再次重传的时候,主动关闭端就接收不到了,就会出现死循环.
TIME_WAIT过多产生原因及危害
正常的TCP客户端连接在关闭后会进入一个TIME_WAIT的状态持续的时间一般在1-4分钟对于连接数不高的场景1-4分钟其实并不长对系统也不会有什么影响但如果短时间内例如1s内进行大量的短连接则可能出现这样一种情况客户端所在的操作系统的socket端口和文件描述符被用尽系统无法再发起新的连接
举例假设每秒建立了1000个短连接Web场景下是很常见的例如每个请求都去访问memcached设TIME_WAIT的时间是1分钟则1分钟内需要建立6W个短连接由于TIME_WAIT时间是1分钟这些短连接1分钟内都处于TIME_WAIT状态都不会释放而Linux默认的本地端口范围配置是net.ipv4.ip_local_port_range 32768 61000不到3W因此这种情况下新的请求由于没有本地端口就不能建立了。
TIME_WAIT过多解决方法
可以改为长连接但代价较大长连接太多会导致服务器性能问题并且安全性也较差
CLOSE_WAIT状态详解
CLOSE_WAIT的意义
比如是客户端要与服务端断开连接先发一个FIN表示自己要主动断开连接了服务端会先回一个ACK这时表示客户端没数据要发了但有可能服务端数据还没发完所以要经历一个close_wait等待服务端数据发送完再回一个FIN和ACK。
CLOSE_WAIT产生太多原因
close_wait 按照正常操作的话应该很短暂的一个状态接收到客户端的fin包并且回复客户端ack之后会继续发送FIN包告知客户端关闭关闭连接之后迁移到Last_ACK状态。但是close_wait过多只能说明没有迁移到Last_ACK也就是服务端是否发送FIN包只有发送FIN包才会发生迁移所以问题定位在是否发送FIN包。FIN包的底层实现其实就是调用socket的close方法这里的问题出在没有执行close方法。说明服务端socket忙于读写。
CLOSE_WAIT太多解决方法
socket读控制 当读取的长度为0时读到结尾立即close。如果read返回-1出现错误检查error返回码有三种情况INTR被中断可以继续读取WOULDBLOCK表示当前socket_fd被阻塞了AGAIN表示现在没有数据稍后重新读取。如果不是AGAIN立即close。
TCP三次握手和四次挥手的11种状态 LISTEN: 首先服务端需要打开一个socket进行监听状态为LISTEN。SYN_SENT: 客户端通过应用程序调用connect进行active open.于是客户端tcp发送一个SYN以请求建立一个连接.之后状态置为SYN_SENT。SYN_RECV: 服务端应发出ACK确认客户端的SYN,同时自己向客户端发送一个SYN. 之后状态置为SYN_RECVESTABLISHED: 代表一个打开的连接双方可以进行或已经在数据交互了。FIN_WAIT1: 主动关闭(active close)端应用程序调用close于是其TCP发出FIN请求主动关闭连接之后进入FIN_WAIT1状态。CLOSE_WAIT:被动关闭(passive close)端TCP接到FIN后就发出ACK以回应FIN请求(它的接收也作为文件结束符传递给上层应用程序),并进入CLOSE_WAIT。FIN_WAIT2: 主动关闭端接到ACK后就进入了FIN-WAIT-2。LAST_ACK:被动关闭端一段时间后接收到文件结束符的应用程序将调用CLOSE关闭连接。这导致它的TCP也发送一个FIN,等待对方的ACK.就进入了LAST-ACK。TIME_WAIT: 在主动关闭端接收到FIN后TCP就发送ACK包并进入TIME-WAIT状态。CLOSING: 这种状态比较特殊实际情况中应该是很少见属于一种比较罕见的例外状态。正常情况下当你发送FIN报文后按理来说是应该先收到或同时收到对方的 ACK报文再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后并没有收到对方的ACK报文反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢其实细想一下也不难得出结论那就是如果双方几乎在同时close一个SOCKET的话那么就出现了双方同时发送FIN报文的情况也即会出现CLOSING状态表示双方都正在关闭SOCKET连接。CLOSED: 被动关闭端在接受到ACK包后就进入了closed的状态。连接结束