zoukankan      html  css  js  c++  java
  • TCP协议入门

    TCP协议入门

    HTTP协议与TCP协议

    TCP协议对应于传输层,HTTP协议对应于应用层,所以TCP协议比HTTP协议在网络模型上低一层。

    TCP报文首部

    1. 源端口与目的端口:各占2个字节,分别写入源端口和目的端口;
    2. 序列号seq:占4个字节,TCP连接中传送的字节流中的每个字节都按照顺序编号。例如:一段报文的字段值是301,而携带的数据有100个字段,显然县一个报文段(如果还有)的数据序号应该加100个字段,从401开始;
    3. 确认号ack:占4个字节,是期望收到对方下一个报文的第一个数据字节的序号。例如:B收到了A发送过来的报文,其序列号字段是501,而数据长度是200字节,这表明B正确的收到了A发送的到序号700为止的数据**。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号设置为701;(要注意ACK和ack是不同的)
    4. 数据偏移:占4位,它指出TCP报文的数据距离TCP报文段的起始处有多远;
    5. 保留:占6位,保留今后使用,但目前应都是0;
    6. 紧急URG:当URG=1,表明紧急指针字段有效,告诉系统此报文段中有紧急数据;
    7. 确认ACK:仅当ACK=1时,确认号字段才有效。TCP规定,在连接建立后所有报文的传输都必须把ACK设置为1;
    8. 推送PSH:当两个应该用进程进行交互式通信时,有时在一端的应用希望在键入一个命令后立即就能收到对方的响应,这时候就将PSH设置为1;
    9. 复位RST:当RST=1时,表示TCP连接中出现严重差错,必须释放连接然后在重新建立连接;
    10. 同步SYN:在建立连接时用来同步序号。当SYN=1,ACK=0时,表示是连接请求报文,若同意连接,则响应报文中应该传输SYN=1,ACK=1;
    11. 终止FIN:用来释放连接。当FIN=1,表明此报文发送方的数据已发送完毕并且要求释放连接。
    12. 窗口:占2字节,指的是通知接收方,发送本报文你需要有多大的空间来接受;
    13. 检验和:占2字节,校验首部和数据这两部分;
    14. 紧急指针:占2字节,指出本报文段中的紧急数据的字节数;
    15. 选项:长度可变,定义一些其他的可选的参数;

    TCP连接的建立(三次握手)

    最开始的时候,客户端和服务器都是处于CLOSED状态。主动建立连接的为客户端,被动打开连接的是服务器。

    1. TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的链接请求,此时服务器就进入了LISTEN(监听)状态;
    2. TCP客户进程也是先创建传输控制块TCB,然后向服务器发出请求报文,这时候报文首部中的同步位SYN=1,同时选择一个初始序列号seq=x,此时,TCP客户端进程进入SYN-SENT(同步已发送)状态。TCP协议规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
    3. TCP服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中ACK=1,SYN=1,确认号ACK=x+1,同时也要为自己初始化一个序列号seq=y,此时,TCP服务器进程进入SYN-RCVD(同步收到)状态。TCP协议规定,这个报文也不能携带数据,需要消耗掉一个序号。
    4. TCP客户进程收到确认报文后,还要向服务器给出确认报文。确认报文的ACK=1,SYN=y+1,自己的序列号seq=x+1。此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。TCP协议规定,ACK报文段可以携带数据,如果不携带数据则不消耗序号。
    5. 当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信,TCP连接建立成功。

    为什么TCP客户端最后还要发送一次确认呢?

    • 防止已经失效的链接请求报文突然又送到了服务器,从而产生错误。
    • 如果使用两次握手建立连接,假设有这样一种场景:客户端发送了第一个请求连接并且没有丢失,只是因为在网络节点中滞留的时间太长了,由于TCP的客户端迟迟没有收到报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据然后关闭连接。此时,之前滞留的那一次请求连接,因为网络通畅了到达了服务器,这个报文本该是失效的,但两次握手机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源浪费。
    • 如果采用的是三次握手,就算是那一次的失效报文传送过来,服务器端接收到了那条失效报文并且回复了确认报文,但客户端不会再次发出确认。由于服务器收不到客户端发来的确认报文,服务器端就能知道客户端并没有请求连接。

    TCP连接的释放(四次挥手)

    数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,然后客户端主动关闭,服务器端被动关闭。

    1. 客户端进程发出连接释放报文,并且停止发送数据。连接释放报文首部FIN=1,序列号seq=u(等于前面已经传送过来的数据最后一个字节序号加1)。此时,客户端进入FIN-WAIT-1(终止等待1)状态。TCP协议规定,FIN报文段即使不携带数据也要消耗一个序号。
    2. 服务器接收到连接释放报文,发出确认报文ACK=1,ack=u+1,并且带上自己的序列号seq=v。此时服务器进入CLOSE_WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器方向的连接就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送但服务器端若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
    3. 客户端收到服务器的确认请求后进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(全关闭状态前还需要接收服务器发送最后的数据)
    4. 服务器将最后数据发送完毕后就向客户端发送连接释放报文FIN=1,ack=u+1,seq=w(由于在半关闭状态时服务器可能又发送了一些数据,假定此时序列号为w)。由此服务器进入LAST-ACK(最后确认)状态,等待客户端的确认报文。
    5. 客户端收到服务器的连接释放报文后,必须发出确认报文ACK=1,ack=w+1,序列号seq=u+1。此时客户端进入TIME-WAIT(时间等待状态)。注意此时TCP连接还没有释放,必须经过2*MSL(最长报文寿命)时间后,当客户端撤销相应的TCB后才进入CLOSED状态。
    6. 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

    为什么客户端最后还要等待2MSL?

    • MSL(Maximum Segment Lifetime):TCP协议允许不同的实现可以设置不同的MSL值。
    • 第一,保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器角度看来,我已经发送了FIN+ACK报文且请求断开了。但是客户端还没有给我回应,可能是我发送的请求断开报文没有被客户端收到,于是服务器会再发送一次FIN+ACK报文请求断开连接。而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且重启2MSL计时器。
    • 第二,防止类似于"三次握手"中提到了的"已经失效的连接请求报文段"出现在本连接中。客户端发完最后一个确认报文后,在这个2MSL时间中,就可以等到本连接持续时间所产生的所有报文段都从网络中消失。这样新的连接中就不会出现旧连接的请求报文。

    为什么建立连接时三次握手,关闭连接是四次挥手呢?

    • 建立连接的时候,服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发给客户端。
    • 而关闭连接时,服务器收到客户端的FIN报文时,仅仅表示对方不再发送数据了,但是还能接收数据,而服务器端也未必把全部的数据都发送给客户端了,所以服务器端可以立即关闭,也可以发送一些数据给客户端后,再发送FIN报文给对方来表示同意现在关闭连接。因此,服务器端ACK和FIN会分开发送,从而导致多了一次报文发送。

    如果已经建立了连接,但客户端突然出故障了怎么办?

    • TCP协议还规定了一个保活计时器。显然,客户端如果出现故障,服务器不能一直白等下去浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,计时器的存货时间通常是设置为2小时,若2小时没有收到客户端的任何数据,服务器端就会给客户端发送一个探测字段,然后每隔75秒再发送一次。若一连发送10个探测报文仍没有任何反应,服务器就认为客户端除了故障,接着就关闭连接。

    大写ACK与小写ack的关系

    • 大写ACK(Acknowledgement)是标识位,可通过它标识报文性质:[ACK] or [SYC] or [FIN]
    • 小写的ack(Acknowledgement Number)是确认号,即收到seq=x这样的报文后,回复ack=x+1的确认号。

    参考资料

    1. 两张动图-彻底明白TCP的三次握手与四次挥手_qzcsu的博客-CSDN博客_三次握手四次挥手详解
  • 相关阅读:
    Codeforces 1291 Round #616 (Div. 2) B
    总结
    刷新DNS解析缓存+追踪+域名解析命令
    数学--数论--Hdu 5793 A Boring Question (打表+逆元)
    Lucene.net(4.8.0) 学习问题记录六:Lucene 的索引系统和搜索过程分析
    LeetCode 117 Populating Next Right Pointers in Each Node II
    LeetCode 116 Populating Next Right Pointers in Each Node
    test test
    LeetCode 115 Distinct Subsequences
    LeetCode 114. Flatten Binary Tree to Linked List
  • 原文地址:https://www.cnblogs.com/keep250/p/14768160.html
Copyright © 2011-2022 走看看