Tizeng's blog Ordinary Gamer

TCP/UDP通信协议(待完成)

2019-03-01
Tizeng

OSI模型

开放式系统互联通信参考模型(英语:Open System Interconnection Reference Model,缩写为 OSI),简称为OSI模型(OSI model),一种概念模型,由国际标准化组织提出,一个试图使各种计算机在世界范围内互连为网络的标准框架。

七层:应用层、表示层、会话层、传输层、网络层、数据链接层、物理层

MAC地址

网络层、IP协议、子网掩码

传输层、端口

“传输层”的功能,就是建立”端口到端口”的通信。相比之下,”网络层”的功能是建立”主机到主机”的通信。

TCP模型将七层模型简化为四层,保留其中的传输层、网络层,将应用层、表示层、会话层归为应用层,将物理层和数据链接层归为网络访问层。

ARP协议

UDP

用户数据包协议(英语:User Datagram Protocol,缩写:UDP),又称用户数据包协议,是一个简单的面向数据报的传输层协议。

无连接

UDP 是不需要和 TCP一样在发送数据前进行三次握手建立连接的,想发数据就可以开始发送了。并且也只是数据报文的搬运工,不会对数据报文进行任何拆分和拼接操作。

不可靠性

不可靠性体现在无连接上,通信都不需要建立连接,想发就发,这样的情况肯定不可靠。 并且收到什么数据就传递什么数据,并且也不会备份数据,发送数据也不会关心对方是否已经正确接收到数据了。 再者网络环境时好时坏,但是 UDP 因为没有拥塞控制,一直会以恒定的速度发送数据。即使网络条件不好,也不会对发送速率进行调整。这样实现的弊端就是在网络条件不好的情况下可能会导致丢包,但是优点也很明显,在某些实时性要求高的场景(比如电话会议)就需要使用 UDP 而不是 TCP。

UDP 头部包含了以下几个数据:

  • 两个十六位的端口号,分别为源端口(可选字段)和目标端口

  • 整个数据报文的长度

  • 整个数据报文的检验和(IPv4 可选 字段),该字段用于发现头部信息和数据中的错误

因此 UDP 的头部开销小,只有八字节,相比 TCP 的至少二十字节要少得多,在传输数据报文时是很高效的

摘自作者:浪里行舟的掘金博客

TCP

传输控制协议(英语:Transmission Control Protocol,缩写:TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,其中流指的是不间断的数据结构。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,用户数据报协议(UDP)是同一层内另一个重要的传输协议。

在因特网协议族(Internet protocol suite)中,TCP层是位于IP层之上,应用层之下的中间层。不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是IP层不提供这样的流机制,而是提供不可靠的包交换。

TCP协议的运行可划分为三个阶段:连接创建(connection establishment)、数据传送(data transfer)和连接终止(connection termination)。

简单说,TCP 协议的作用是,保证数据通信的完整性和可靠性,防止丢包

TCP_package

数据通过以太网数据包、IP数据包、TCP数据包传输,最后才是应用层数据包。

发送的时候,TCP 协议为每个包编号(sequence number,简称 SEQ),以便接收的一方按照顺序还原。万一发生丢包,也可以知道丢失的是哪一个包。每个数据包都可以得到两个编号:自身的编号,以及下一个包的编号。接收方由此知道,应该按照什么顺序将它们还原成原始文件。

TCP 协议为了做到效率与可靠性的统一,设计了一个慢启动(slow start)机制:即最开始先发送少量的包(一般为10个),根据丢包的情况,调整发送的速率。

默认情况下,接收方每收到两个TCP数据包,就要发送一个确认信息 ACK ,它携带两个信息:

  • 期望收到下一个数据包的编号

  • 接受窗口的剩余容量

TCP_Ack

上图的例子可以帮助我们理解,第一次通讯A主机的数据包编号是1,ACK编号也是1,长度为100,因此第二次B到A的通讯数据包编号为1(A的ACK编号),ACK编号为101,(1+100),长度为200,由此得出第三次A到B的通讯数据包编号为101(B的ACK编号),ACK编号为201(1+200),长度为50,以此类推。

这样一来,每一个数据包都带有下一个数据包的编号,如果下一个数据包没有收到,那么 ACK 的编号就不会发生变化。

举例来说,现在收到了4号包,但是没有收到5号包。ACK 就会记录,期待收到5号包。过了一段时间,5号包收到了,那么下一轮 ACK 会更新编号。如果5号包还是没收到,但是收到了6号包或7号包,那么 ACK 里面的编号不会变化,总是显示5号包。这会导致大量重复内容的 ACK。

如果发送方发现收到三个连续的重复 ACK,或者超时了还没有收到任何 ACK,就会确认丢包,即5号包遗失了,从而再次发送这个包。通过这种机制,TCP 保证了不会有数据包丢失。

TCP_ACK

如图所示,如果接收方一直没有收到期望 ACK 值的包,便会一直发送这个值的ACK,直至收到 seq 为此 ACK 的包为止。

三次握手和四次挥手

所谓三次握手(Three-way Handshake),是指建立一个 TCP 连接时,需要客户端和服务器总共发送3个包。而四次挥手是指在客户端与服务端断开连接时需要发送4个包作为确认。

  • SYN:同步序列编号(Synchronize Sequence Numbers),是TCP/IP建立连接时使用的握手信号

  • FIN为关闭连接的请求,和SYN一样默认占用一字节

建立连接时,首先客户端发送SYN=1和初始seq=x的包,进入SYN_SEND状态,服务器收到后发送SYN+ACK(均为1)、seq=y应答,进入SYN_RCVD状态,同时将确认序号ACKnum设为x+1,这是第二次握手,之后客户端再发送确认包ACK并设ACKnum=y+1,发送完毕后,客户端进入ESTABLISHED状态,当服务器收到这个包后,也进入这个状态,三次握手结束,TCP连接建立。之后就可以进行前面的通信了。

TCP_win

图中的虚线框便是滑动窗口,当数据被取走,窗口就会变大、右移。

如果要断开连接,需要有一方发送FIN和ACK、seq,并进入FIN_WAIT_1状态,表示自己没有数据要发送了,但还可以接收数据,服务器收到之后(第一次挥手),发送一个确认包,表示已经收到请求,但可能还需要发送一些数据,进入CLOSE_WAIT状态,当客户端收到这个确认包后,则进入FIN_WAIT_2状态,等待服务器的断开请求,这是第二次挥手。当服务器准备好断开连接时,发送FIN包,进入LAST_ACK状态,等待客户端最后的确认ACK,客户端收到请求后(第三次挥手),发送ACK确认并进入TIME_WAIT状态,等待可能出现的重传ACK,注意这里会等待两个最大段生命周期(2MSL,2 Maximum Segment Lifetime)之后,若没有收到服务器的ACK,则认为服务器已经关闭,于是自己也关闭连接,进入CLOSED状态,服务器端收到确认包后自己也会进入CLOSED状态,TCP连接断开,四次挥手结束。

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

常见问题:

  1. 为什么建立连接时要三次握手而不是两次?

    在谢希仁的《计算机网络》中是这样说的:

    为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。

    在书中同时举了一个例子,如下:

    “已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”

  2. 为什么断开连接的最后 A 要等 2MSL 才从TIME_WAIT状态变为CLOSED状态?

    这是为了确保 B 收到收到 A 发送的最后的 ACK 报文,因为这个报文有可能丢失,因而处在LAST_ACK状态的 B 就得不到对已发送FIN报文的确认,B 就会重发这段报文,而 A 会在 2MSL 内收到这个重传的报文,然后 A 会重传一次 ACK 确认并重新开始计时。这样可以避免 B 发送完FIN后得不到 A 的确认而一直等待。

  3. 为什么断开连接要四次挥手而建立连接只要三次?

    因为建立连接时 B 可以将同步报文 SYN 和确认报文 ACK 一起发送,而断开连接时是先发送 ACK 确认收到了断开请求,这时可能还有数据需要传,因此FIN在剩余数据传送完毕后才发送。

TCP与UDP的对比

TCP_UDP_diff

参考:阮一峰的博客和其他资料


下一篇 C++基础

Comments

Content