zoukankan      html  css  js  c++  java
  • TCP实战

    什么是TCP

    传输控制协议TCP简介:

    1、面向连接的、可靠的、基于字节流的传输层通信协议

    2、将应用层的数据流分割成报文段并发送给目标节点的TCP层

    3、数据包都有序号(seq),对方收到则发送ACK确认,未收到则重传

    4、使用校验和来检验数据在传输过程中是否有误

    OSI七层模型与tcp协议族

    模型以及协议2

    网络中的数据传输过程

    数据传输封装过程

    TCP报文头详解:

    报文头

    源端口:

    ​ 指定了发送端的端口

    目的端口:

    ​ 指定了接受端的端口号

    序号(Seq 4个字节):

    ​ 指明了段在即将传输的段序列中的位置

    ​ 假如序列号字段是108 携带的数据有100个字段,下一个报文序列号就是(108+100)也就是208开始

    确认号(Ack):

    ​ 规定成功收到段的序列号,确认序号包含发送确认的一端所期望收到的下一个序号

    ​ 假设B收到A发来的报文,其序列号是301 数据长度是200字节 ,那么B期望收到A的下个序号是501,那么B发给A的确认报文ack就是501

    TCP偏移量(offset):

    ​ 指定了段头的长度。段头的长度取决与段头选项字段中设置的选项,tcp报文数据距离 tcp报文起始处有多远

    保留(reserved):

    ​ 指定了一个保留字段,以备将来使用

    标志(TCP Flags):

    SYN: 表示同步
    
     ACK: 表示确认
    
      PSH: 表示尽快的将数据送往接收进程
    
     RST: 表示复位连接
    
      URG: 表示紧急指针
    
      FIN: 表示发送方完成数据发送
    

    窗口(window):

    ​ 滑动窗口大小,用来告知发送端和接收端的缓存大小,以此达到数据传输的速率控制,以此达到流量控制。指定关于发送端能传输的下一段的大小的指令

    校验和(checksum):

    ​ 奇偶校验,校验和包含TCP段头和数据部分,用来校验段头和数据部分的可靠性

    ​ 由发送端计算和存储,由接收端验证

    紧急指针:

    ​ 指明段中包含紧急信息,只有当U R G标志置1时紧急指针才有效

    选项:

    ​ 指定了公认的段大小,时间戳,选项字段的末端,以及指定了选项字段的边界选项

    TCP滑动窗口和回退n帧协议

    RTT和RTO

    >RTT∶发送一个数据包到收到对应的ACK,所花费的时间

    >RTO∶重传时间间隔(规定时间内没有收到ack的时间就会重传),这个值根据RTT计算而来

    TCP拥塞控制

    拥塞控制就是防止过多的数据注入到网络中,这样使得网络中的路由器或者链路不至于过载。TCP拥塞控制方法主要包括:慢开始,拥塞避免,快重传和快恢复。
    慢开始是指发送方先设置cwnd=1,一次发送一个报文段,随后每经过一个传输轮次,拥塞串口cwnd就加倍,其实增长并不慢,以指数形式增长。还要设定一个慢开始门限,当cwnd>门限值,改用拥塞避免算法。拥塞避免算法使cwnd按线性规律缓慢增长。当网络发生延时,门限值减半,拥塞窗口执行慢开始算法。
    之后又提出了快重传和快恢复
    当接收方收到失序的报文段,按照快重传,需要尽快发送对未收到的报文段的重复确认。快恢复是指当拥塞串口达到门限值,不直接开启慢启动算法,而是快恢复,快恢复就是收到三个重复的确认(可看作是网络已经拥塞了),此时并不执行慢开始算法,而是执行快恢复,就是新的门限值是原来的一半,直接进入拥塞避免阶段。

    滑动窗口协议:

    以段为单位发送数据

    ​ 在建立 TCP 连接的同时,也可以确定发送数据包的单位,我们也可以称其为“最大消息长度”(MSS)。最理想的情况是,最大消息长度正好是 IP 中不会被分片处理的最大数据长度。

    ​ TCP 在传送大量数据时,是以 MSS 的大小将数据进行分割发送。进行重发时也是以 MSS 为单位。

    ​ MSS 在三次握手的时候,在两端主机之间被计算得出。两端的主机在发出建立连接的请求时,会在 TCP 首部中写入 MSS 选项,告诉对方自己的接口能够适应的 MSS 的大小。然后会在两者之间选择一个较小的值投入使用。

    利用窗口控制提高速度

    TCP 以1个段为单位,每发送一个段进行一次确认应答的处理。这样的传输方式有一个缺点,就是包的往返时间越长通信性能就越低。

    为解决这个问题,TCP 引入了窗口这个概念。确认应答不再是以每个分段,而是以更大的单位进行确认,转发时间将会被大幅地缩短。也就是说,发送端主机,在发送了一个段以后不必要一直等待确认应答,而是继续发送。如下图所示:

    1856419-3883cce343404099

    窗口大小就是指无需等待确认应答而可以继续发送数据的最大值。上图中窗口大小为4个段。这个机制实现了使用大量的缓冲区,通过对多个段同时进行确认应答的功能。

    滑动窗口控制

    1856419-7919bd12b5b2917b

    上图中的窗口内的数据即便没有收到确认应答也可以被发送出去。不过,在整个窗口的确认应答没有到达之前,如果其中部分数据出现丢包,那么发送端仍然要负责重传。为此,发送端主机需要设置缓存保留这些待被重传的数据,直到收到他们的确认应答。

    在滑动窗口以外的部分包括未发送的数据以及已经确认对端已收到的数据。当数据发出后若如期收到确认应答就可以不用再进行重发,此时数据就可以从缓存区清除。

    收到确认应答的情况下,将窗口滑动到确认应答中的序列号的位置。这样可以顺序地将多个段同时发送提高通信性能。这种机制也别称为滑动窗口控制。

    回退n帧协议:

    ​ 滑动窗口协议是理论,可以用的是后退n针协议。发送方一次发送比如说10个帧,前两个帧都返回了对应的ACK,数据帧2出现了错误,这时发送方被迫重新发送2-8这七个帧,这就是回退n帧协议。但是如果接收方已经接收到了3-8帧,只是丢失了2帧,全部重传太浪费网络条件了,所以有时候会选择重传丢失的的帧。这就是选择重传协议。如下图所示

    1856419-0732e3d90fea80c8

    TCP三次握手

    TCP三次握手

    在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。

    第一次握手∶建立连接时,客户端发送SYN包(seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;

    第二次握手∶服务器收到SYN包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;

    第三次握手∶客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED 状态,完成三次握手。

    Q:为什么需要三次握手才能建立起连接?

    为了初始化Sequence Number的初始值

    三次握手的核心就是交换seq,ack=seq+1表示交换seq成功

    通信的双方要通知对方自己的seq序号,是作为以后数据通信的序号,保证数据不会因为网络上的传输问题而乱序,tcp会通过找个序号来拼接数据

    Q:首次握手的隐患——SYN超时

    SYN超时问题起因分析

    > Server收到Client的SYN,回复SYN-ACK的时候未收到ACK确认

    > Server不断重试直至超时(默认重复5次),Linux默认等待(1+2+4+8+16+32)=63秒才断开连接

    针对SYN Flood的防护措施

    > SYN队列满后,通过tcp_syncookies参数回发SYN Cookie

    > 若为正常连接则Client会回发SYN Cookie,直接建立连接

    Q:建立连接后,Client出现故障怎么办保活机制

    向对方发送保活探测报文,如果未收到响应则继续发送

    尝试次数达到保活探测数仍未收到响应则中断连接

    TCP四次挥手

    四次挥手

    TCP采用四次挥手来释放连接

    第一次挥手∶ Client发送一个FIN,用来关闭Client 到Server 的数据传送,Client进入 FIN_WAIT_1状态;

    第二次挥手∶Server收到FIN后,发送一个ACK给 Client,确认序号为收到序号+1(与SYN 相同,一个FIN 占用一个序号),Server进入 CLOSE_WAIT状态;

    第三次挥手∶Server 发送一个FIN,用来关闭Server 到 Client 的数据传送,Server进入 LAST_ACK状态;

    第四次挥手∶Client收到 FIN后,Client进入TIME_WAIT 状态,接着发送一个ACK给 Server,确认序号为收到序号+1,Server进入 CLOSED状态,完成四次挥手。

    Q:为什么会有TIME_WAIT状态?

    > 确保有足够的时间让对方收到ACK包 数据以来一回就是2个MSL

    >避免新旧连接混淆

    Q:为什么服务器出现大量CLOSE_WAIT状态?

    原因是对方关闭socket连接,我方忙于读或写,没有及时关闭连接

    > 检查代码,特别是释放资源的代码(未释放资源)

    > 检查配置,特别是处理请求的线程配置(线程池中的线程数配置不合理)

    linux查看服务器close_wait状态语句

    获取当前服务器处于各个状态下的连接数

    netstat -n | awk '/^tcp/{++S[$NF]}END{for(a in S) print a,S[a]}'
    

    TIME_WAIT 1507
    CLOSE_WAIT 588
    FIN_WAIT 2 5
    ESTABLISHED 413

    CLOSE_WAIT 如果有几千的话就要去排查问题了,linux会为用户提供有限的文件句柄数,链接和文件句柄是一一对应的,也就是说会无法处理新的请求,请求坑位没有了。新的请求无法被处理

    Q:为什么需要四次握手才能断开连接?

    因为全双工,发送方和接收方都需要FIN报文和ACK报文

  • 相关阅读:
    [BZOJ4480] JSOI2013 快乐的jyy
    [BZOJ4755] JSOI2016 扭动的回文串
    [BZOJ4754] JSOI2016 独特的树叶
    [BZOJ5179] JSOI2011 任务调度
    [BZOJ4320] SHOI2006 Homework
    [AT2300] Snuke Line
    [洛谷P3974] TJOI2015 组合数学
    [CF331E] Biologist
    [洛谷P4249] WC2007 剪刀石头布
    【BZOJ4548】小奇的糖果
  • 原文地址:https://www.cnblogs.com/yslu/p/15692089.html
Copyright © 2011-2022 走看看