zoukankan      html  css  js  c++  java
  • TCP小结

    TCP/IP协议实现了不同主机,不同操作系统之间信息交流。由4层构成,从上往下依次为:

    1、应用层,包括http,ftp等协议,用于实现某一项具体的功能。

    2、传输层,包括TCP和UDP,一个可靠,一个快速。

    3、网络层,IP协议,完成IP数据包在网络中的传输,但不可靠。

    4、数据链路层,主要用于接收和发送IP数据包。

    在网络编程中主要接触TCP,UDP协议。这里对TCP的有关知识做一下小结:

    TCP是面向连接的,可靠的传输协议:通过3次握手建立连接,4次挥手断开连接,通过重传机制实现可靠性,此外,通过滑动窗口实现流量控制,还通过一些算法避免网络拥塞。

    首先,复习一下TCP头格式:

     

    • TCP连接用四元组表示一个TCP连接:Sre_IP,Sre_port,Des_IP,Des_port;其中Sre_IP和Des_IP在IP协议头中。
    • Sequence Number是包中字节的序列号,防止网络包乱序。Acknowledgement Number确认已收到的连续最大序列号。
    • Window就是滑动窗口,用于流量控制。
    • TCP Flags:
      • U   Urgent
      • A   Ack
      • P   Push
      • R   Reset
      • S   Syn
      • F   Fin

      一、TCP的状态机

    1、TCP的连接和断开伴随着客户端和服务器端的状态变化:

      服务器端主动监听一个端口进入LISTEN状态;客户端发送Syn包进入SYN SEND状态;服务器端收到Syn包,发送Syn包及Ack进入SYN RECEIVED状态;客户端收到服务器的Syn+Ack包,返回一个Ack进入ESTABLISHED状态;服务器端收到Ack后也进入ESTABLISHED状态。三次握手过程中,SYN SEND和SYN RECEIVE状态很短暂,很难捕捉。大部分看到的是LISTEN和ESTABLISHED状态

      客户端发送Fin包,主动断开连接,进入FIN_WAIT_1状态;服务器端收到Fin,返回一个Ack包进入CLOSE_WAIT状态;客户端收到Ack包,进入FIN_WAIT_2状态;至此,TCP连接进入了半连接状态,即客户端已经主动关闭连接,不在发送数据,但是服务器端仍然可以向客户端发送数据。服务器端在应用层要做出判断,如果没有数据要发送了就会执行断开连接操作(调用close)。服务器端发送Fin包,进入LAST_ACK状态;客户端收到Fin,回Ack进入TIME_TAIT状态;服务器端收到Ack,进入CLOSED状态;客户端的TIME_WAIT状态时间到了后,也进入CLOSED状态。服务器端和客户端都可能主动断开连接(其实,连接建立以后就不区分客户端和服务器端了),不过大多是客户端主动断开。

    2、三次握手:主要是为了初始化Sequency Number的初始值,作为通信的初始序列号。而初始值是根据一个假时钟赋值的,没必要深究。

    3、四次挥手:因为TCP连接时全双工,所以两端都要发送一个Fin包,收到一个Ack包。TIME_WAIT状态持续的时间是2倍的MSL,MSL表示TCP报文在网络最长的存活时间,linux设置为30s。在TIME_WAIT状态时,TCP连接不会接受任何TCP报文,除了Fin报文,并且端口也不能给别的TCP连接使用。原因 一)防止最后一个ACK报文丢失,如果丢失,对方会再发送一次Fin。二)防止该端口被重用,上一个连接迟到的数据包和新连接的数据包混淆。

    4、同时连接和同时关闭:一)同时连接发生的概率很小,而且还有点不可思议。我向你发起连接,你不监听也就罢了,居然还要和我连接,而且你要连接的端口正好是我用来和你连接的端口。不管怎么说,TCP支持这种情况,同时连接时双方几乎同时发出Syn进入SYN SEND状态,收到对方的Syn报文会从SYN SEND状态变成SYN RECEIVED状态,收到自己Syn的Ack后进入ESTABLISHED状态。二) 同时关闭的概率还是挺大的,双方同时发出Fin,进入FIN_WAIT_1状态;收到对方的Fin,进入CLOSING状态;收到自己Fin的Ack进入TIME_WAIT状态。

    二、重传机制

    1、双方建立连接时,互通了Seq Num,然后开始通信。发送端向接收端发送TCP报文,接收端根据接收到的报文的Seq Num回Ack包,Ack是已经收到的连续字节最大序号的下一个序号。如果发送的某一个报文丢失了,发送端要重传该报文,比如:发送端发送了序号为1、2、3、4、5的TCP包,接收端收到了1、2、4、5。所以,接收方只回2的ack。

    • 超时重传

      发送一个报文就启动一个超时定时器,当timeout到了时,3的ack还没回来,就重发。可能只重发3,也可能重发3,4,5.这要看具体实现。第一种慢,但省带宽;第二种快,但浪费带宽,还可能做无用功。但都是基于超时重传。

    • 快速重传

      不以时间驱动,而是以数据驱动。当发送方收到3个重复的确认就会重发相应的数据包。比如,当2到达时,发送方会收到2数据包的确认;4,5到达时,发送方还是收到2的确认,这时发送方会重传数据包3或重传3,4,5……(因为可能6,7到了呢).

    •  SACK法

      就是在回复的ack头里添加一个Sack的东西,里面有接收方已经收到数据的情况,防止发送方重传不必要的数据。但是接收方也可能把已经接收到的数据扔掉(让出内存给别的进程),这回导致发送方的sack数据不准,所以,发送方不能完全依赖sack,还要依赖timeout。

      2、timeout的设置:

      基本的方法是取样RTT,然后进过一些公式的计算,得出timeout的值。

      三、流量控制

      1、滑动窗口是实现网络流量控制的一项技术,接收方通过告知发送方自己可用缓存区的大小来限制发送方数据的发送。下面是TCP发送缓存区和接受缓存区的数据结构:

    • 接收端,LastByteRead是应用层上次读的位置,NextByteExpected是上次Ack的位置,LastByteRcvd是收到包的最后一个位置,中间还有一些没收到。
    • 发送方,LastByteAcked是上次确认的位置,LastByteSend是已经发送出去的最后一个位置,LastByteWritten是应用层写到的位置。
    • Window = MaxRcvBuff - LastByteRcvd - 1

    2、Zero Window

      当接收方的window为0时,发送方不能再发数据。过一段时间(一般为30s-60s),发送方会发一个ZWP包,让接收方ack,以获取新的window大小。如果3次都为0,有的TCP实现会发送reset断开连接。

    3、silly window Syndrome

    糊涂窗口综合症,window给的值太小,导致TCP报文携带的数据太少,浪费带宽。解决方法:一)接收端的可用缓冲区如果小于某值 (视具体实现定)就回window的大小为0,等可用的接收缓冲区变大后再通知发送方。二)发送方执行Nagle算法:windows size > MSS或者等待时间超过200ms就发数据。

    四、拥塞处理

    通过滑动窗口做流量控制还不够,因为数据在中间的传输过程没发控制。数据在传输过程中可能会发生网络拥塞,所以还需要拥塞处理相关的流量控制。

    拥塞控制主要是四个算法:1)慢启动,2)拥塞避免,3)拥塞发生时的快速重传,4)快速恢复

  • 相关阅读:
    iOS BCD编码
    Java泛型解析(02):通配符限定
    HDU 5317 RGCDQ(素数个数 多校2015啊)
    通过getElementById来取得Form里的表单元素
    关系型数据库工作原理-快速缓存(翻译自Coding-Geek文章)
    cocos2d-x之道~制作第一款文字游戏(二)
    Html5实现手机九宫格password解锁功能
    [Python] 字典推导 PEP 274 -- Dict Comprehensions
    NYOJ 36 最长公共子序列 (还是dp)
    【设计模式】学习笔记5:工厂模式
  • 原文地址:https://www.cnblogs.com/leng2052/p/5277410.html
Copyright © 2011-2022 走看看