• 数据连接linux网络编程之TCP/IP基础(四):TCP连接的建立和断开、滑动窗口


    在写这篇文章之前,xxx已经写过了几篇关于改数据连接主题的文章,想要了解的朋友可以去翻一下之前的文章

        一、TCP段格式:

        TCP的段格式如下图所示

        数据和连接

        源端口号与目标端口号
    源端口号和目标端口号,加上IP首部的源IP地址和目标IP地址唯一确定一个TCP连接。

        
    序号
    序号表示在这个报文段中的第一个数据字节序号。

        
    确认号
    仅当ACK标记为1时有效。确认号表示期望收到的下一个字节的序号。

        
    头部长度
    4位,TCP头部最多60个字节,最少20个字节

        
    保存位
    6位,必须为0

        

        6个标记位
    URG-紧急指针有效
    ACK-确认序号有效
    PSH-接收方应尽快将这个报文段交给应用层
    RST-连接重置
    SYN-同步序号用来发起一个连接
    FIN-表示将要终止一个连接

        窗口大小
    通过窗口大小来达到流量控制。

        校验和
    对tcp表头与数据停止校验。

        

        紧急指针
    是一个正的偏移量,与序号字段中的值相加表示紧急数据最后一个字节的序号。TCP的紧急方式是发送端向另一端发送紧急数据(也称为带外数据)的一种方式。

        选项与填充(选项为4字节整数倍,否则用0填充)
    最常见的可选字段是最长报文大小MSS(Maximum Segment Size),每一个连接方平日都在通信的第一个报文段中指明这个选项。它指明本端所能接收的最大长度的报文段(payload)。该选项如果不设置,默以为536(20+20+536=576字节的IP数据报),其中ip首部和tcp首部各20个字节,而internet 上标准的MTU (最小)为576B。

        

        二、通信时序(3次握手-->传输数据-->4次挥手)

        下图是一次TCP通信的时序图:

        数据和连接

        

        在这个例子中,首先客户端主动发起连接、发送请求,然后服务器端响应请求,然后客户端主动关闭连接。两条竖线表示通信的两头,从上到下表示时间的先后顺序,注意,数据从一端传到网络的另一端也需要时间,所以图中的箭头都是斜的。双方发送的段按时间顺序编号为1-10,各段中的重要信息在箭头上标出,例如段2的箭头上标着SYN, 8000(0), ACK 1001, <mss 1024>,表示该段中的SYN位置1,32位序号是8000,该段不携带有效载荷(数据字节数为0),ACK位置1,32位确认序号是1001,带有一个mss选项值为1024。

        

        建立连接的过程:
    1. 客户端收回段1,SYN位表示连接请求。序号是1000,这个序号在网络通信中用作临时的地址,每发一个数据字节,这个序号要加1,这样在接收端可以根据序号排出数据包的正确顺序,也可以发明丢包的情况,另外,规定SYN位和FIN位也要占一个序号,这次虽然没发数据,但是由于发了SYN位,因此下次再发送应该用序号1001。mss表示最大段尺寸,如果一个段太大,封装成帧后超过了链路层的最大帧长度,就必须在IP层分片,为了避免这种情况,客户端声明自己的最大段尺寸,提议服务器端发来的段不要超过这个长度。
    2. 服务器收回段2,也带有SYN位,同时置ACK位表示确认,确认序号是1001,表示“我接收到序号1000及其之前所有的段,请你下次发送序号为1001的段”,也就是应答了客户端的连接请求,同时也给客户端收回一个连接请求,同时声明最大尺寸为1024。
    3. 客户端收回段3,对服务器的连接请求停止应答,确认序号是8001。

        

        在这个过程中,客户端和服务器分别给对方发了连接请求,也应答了对方的连接请求,其中服务器的请乞降应答在一个段中收回,因此一共有三个段用于建立连接,称为'''三方握手(three-way-handshake)'''。在建立连接的同时,双方协商了一些信息,例如双方发送序号的初始值、最大段尺寸等。

        
    在TCP通信中,如果一方收到另一方发来的段,读出其中的目标端口号,发明本机并没有任何进程应用这个端口,就会应答一个包括RST位的段给另一方。例如,服务器并没有任何进程应用8080端口,我们却用telnet客户端去连接它,服务器收到客户端发来的SYN段就会应答一个RST段,客户端的telnet程序收到RST段后报告错误Connection timeout:数据和连接

        每日一道理
    记不清有多少个夜晚,在我翻阅纸张的指间滑落;记不清有多少支蜡烛,在我的凝视中化为灰烬。逝者如斯,我时时刻刻会听见自己对生命承诺的余音,感到岁月的流转在渐渐稀释我的年少无知,我愿自己是一只上足了发条的时钟,在昼夜不停的流转中留下自己充实的每一刻。

        

        数据传输的过程:
    1. 客户端收回段4,包括从序号1001开始的20个字节数据。
    2. 服务器收回段5,确认序号为1021,对序号为1001-1020的数据表示确认收到,同时请求发送序号1021开始的数据,服务器在应答的同时也向客户端发送从序号8001开始的10个字节数据,这称为piggyback。
    3. 客户端收回段6,对服务器发来的序号为8001-8010的数据表示确认收到,请求发送序号8011开始的数据。

        在数据传输过程中,ACK和确认序号是非常重要的,应用程序交给TCP协议发送的数据会暂存在TCP层的发送缓冲区中,收回数据包给对方以后,只有收到对方应答的ACK段才晓得该数据包确切发到了对方,可以从发送缓冲区中释放掉了,如果因为网络故障丧失了数据包或者丧失了对方发还的ACK段,经过等待超时后TCP协议主动将发送缓冲区中的数据包重发。

        

        这个例子只描述了最简略的一问一答的情景,现实的TCP数据传输过程可以收发很多数据段,虽然典型的情景是客户端主动请求服务器被动应答,但也不是必须如此,事实上TCP协议为应用层供给了全双工(full-duplex)的服务,双方都可以主动甚至同时给对方发送数据。

        
    如果通信过程只能采用一问一答的方式,收和发两个方向不能同时传输,在同一时间只允许一个方向的数据传输,则称为'''半双工(half-duplex)''',假设某种面向连接的协议是半双工的,则只需要一套序号就够了,不需要通信双方各自维护一套序号。

        

        关闭连接的过程:
    1. 客户端收回段7,FIN位表示关闭连接的请求。
    2. 服务器收回段8,应答客户端的关闭连接请求。
    3. 服务器收回段9,其中也包括FIN位,向客户端发送关闭连接请求。
    4. 客户端收回段10,应答服务器的关闭连接请求。

        

        建立连接的过程是三方握手,而关闭连接平日需要4个段,服务器的应答和关闭连接请求平日分歧并在一个段中,因为有连接半关闭的情况(调用shutdown而不是close),这种情况下客户端关闭连接以后就不能再发送数据给服务器了,但是服务器还可以发送数据给客户端,直到服务器也关闭连接为止。

        

        三、滑动窗口和流量控制

        如果发送端发送的速度较快,接收端接收到数据后处理的速度较慢,而接收缓冲区的大小是牢固的,就会丧失数据。TCP协议通过'''滑动窗口(SlidingWindow)'''机制解决这一问题。看下图的通信过程。

        数据和连接

        1. 发送端发起连接,声明最大段尺寸是1460,初始序号是0,窗口大小是4K,表示“我的接收缓冲区还有4K字节闲暇,你发的数据不要超过4K”。接收端应答连接请求,声明最大段尺寸是1024,初始序号是8000,窗口大小是6K。发送端应答,三方握手结束。
    2. 发送端收回段4-9,每一个段带1K的数据,发送端根据窗口大小晓得接收端的缓冲区满了,因此停止发送数据。
    3. 接收端的应用程序提走2K数据,接收缓冲区又有了2K闲暇,接收端收回段10,在应答已收到6K数据的同时声明窗口大小为2K。

        4. 接收端的应用程序又提走2K数据,接收缓冲区有4K闲暇,接收端收回段11,重新声明窗口大小为4K。
    5. 发送端收回段12-13,每一个段带2K数据,段13同时还包括FIN位。
    6. 接收端应答接收到的2K数据(6145-8192),再加上FIN位占一个序号8193,因此应答序号是8194,接收端同时声明窗口大小为2K。
    7. 接收端的应用程序提走2K数据,接收端重新声明窗口大小为4K。
    8. 接收端的应用程序提走剩下的2K数据,接收缓冲区全空,接收端重新声明窗口大小为6K。
    9. 接收端的应用程序在提走全部数据后,决议关闭连接,收回段17包括FIN位,发送端应答,连接完全关闭。

        
    上图在接收端用小方块表示1K数据,实心的小方块表示已接收到的数据,虚线框表示接收缓冲区,因此套在虚线框中的空心小方块表示窗口大小,从图中可以看出,随着应用程序提走数据,虚线框是向右滑动的,因此称为滑动窗口。

        

        从这个例子还可以看出,发送端是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),在底层通信中这些数据可能被拆成很多数据包来发送,但是一个数据包有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是轻易出现粘包问题的原因。而UDP是面向消息的协议,每一个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。

        

        四、TCP如何保障可靠性

        1、应用数据被分割成TCP以为最适合发送的数据块,称为段传递给IP层。
    2、当TCP收回一个段后,它启动一个定时器,等待目标端确认收到这个报文段。如果不能实时收到一个确认,将重发这个报文段。
    3、当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,平日将推迟几分之一秒。
    4、TCP将坚持它首部和数据的校验和。这是一个端到端的校验和,目标是检测数据在传输过程中的任何变化。如果收到段的校验和有差错,TCP将抛弃这个报文段并且不确认(致使对方超时重传)
    5、TCP承载于IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。TCP将对收到的数据停止重新排序。
    6、IP数据报会产生重复,TCP的接收端必须抛弃重复的数据。
    7、TCP还能供给流量控制。TCP连接的每一方都有必定大小的缓冲空间。

        

        参考:

        《Linux C 编程一站式学习》

        《TCP/IP详解 卷一》

    文章结束给大家分享下程序员的一些笑话语录: 关于编程语言
    如果 C++是一把锤子的话,那么编程就会变成大手指头。
    如果你找了一百万只猴子来敲打一百万个键盘,那么会有一只猴子会敲出一 段 Java 程序,而其余的只会敲出 Perl 程序。
    一阵急促的敲门声,“谁啊!”,过了 5 分钟,门外传来“Java”。
    如果说 Java 很不错是因为它可以运行在所有的操作系统上,那么就可以说 肛交很不错,因为其可以使用于所有的性别上。

    --------------------------------- 原创文章 By
    数据和连接
    ---------------------------------

  • 相关阅读:
    【JLOI 2015】城池攻占
    【BalticOI 2004】Sequence
    罗马游戏
    《STL源码剖析》STL迭代器分类
    《Effective C++》模版与泛型编程
    《Effective C++》继承与面向对象设计
    《Effective C++》实现 章节
    [C++]const_cast,dynamic_cast,reinterpret_cast,static_cast转型
    [C++]default constructor默认构造函数
    [C++]union联合体总结
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3097763.html
Copyright © 2020-2023  润新知