"在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别。就好像圣经中上帝打乱了各地人的口音,让他们无法合作一样。计算机使用者意识到,计算机只是单兵作战并不会发挥太大的作用。只有把它们联合起来,电脑才会发挥出它最大的潜力。于是人们就想方设法的用电线把电脑连接到了一起。
但是简单的连到一起是远远不够的,就好像语言不同的两个人互相见了面,完全不能交流信息。因而他们需要定义一些共通的东西来进行交流,TCP/IP就是为此而生。TCP/IP不是一个协议,而是一个协议族的统称。里面包括了IP协议,IMCP协议,TCP协议,以及我们更加熟悉的http、ftp、pop3协议等等。电脑有了这些,就好像学会了外语一样,就可以和其他的计算机终端做自由的交流了。"
概述
传输控制协议(TCP,Transmission Control Protocol),它是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议
- 我们可以作这样一个类比,TCP的工作过程,就像是打电话,在打电话时首先要进行拨号,对方接通之后才能进行通话,若是对方忙或者其他不能接听的情况,不能建立连接,双方也就不能进行通话,TCP也是如此,只有连接建立之后才能进行通信
- 打电话通话过程中同时能够进行听和说,TCP也是如此,能同时进行收发数据的操作,也就是TCP支持全双工通信
- 打完电话,任何一方挂断,就能结束本次通话,而TCP在传输完数据之后,也是要释放连接的
TCP的主要特点
- TCP 是面向连接的运输层协议,传输数据之前,一定要建立连接并保持连接,传输完成也要释放连接
- 每一条 TCP 连接只能有两个端点 (endpoint),每一条 TCP 连接只能是点对点的(一对一)(UDP是可以一对一、一对多、多对一和多对多的交互通信)
- TCP 提供可靠交付的服务,能保证数据传输时没有差错的,顺序也是对的
- TCP 提供全双工通信,可以同时进行收发数据的操作
- TCP是面向字节流的
面向字节流:
- TCP 中的“流”(stream) 指的是流入或流出进程的字节序列
- "面向字节流"的含义是:虽然应用程序和 TCP 的交互是一次一个数据块,但 TCP 把应用程序交下来的数据看成仅仅是一连串无结构的字节流,也就是说其实数据都是有一定结构的,但是在TCP眼里它都是无结构的字节串
- TCP 不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系,也就是说发送方发送一定规格和数量的数据块,接收方不一定最后收到的是同样规格和数量的数据块,但是能保证的是最后得到的数据,一定是无差错的
- 但接收方应用程序收到的字节流必须和发送方应用程序发出的字节流完全一样,在发送过程中到底如何分块,TCP不关心,TCP只关心最后数据肯定是和发送方发送过来的数据完全一样
注意:
- TCP 连接是一条虚连接而不是一条真正的物理连接
- TCP 对应用进程一次把多长的报文发送到 TCP 的缓存中是不关心的
- TCP 根据对方给出的窗口值和当前网络拥塞的程度来决定一个报文段应包含多少个字节(UDP 发送的报文长度是应用进程给出的,不管给出多长,UDP都不进行合并、拆分等操作)
- TCP 可把太长的数据块划分短一些再传送,TCP 也可等待积累有足够多的字节后再构成报文段发送出去
TCP报文段的首部格式
- 源端口和目的端口字段——各占 2 字节。端口是运输层与应用层的服务接口。运输层的复用和分用功能都要通过端口才能实现
- 序号字段——占 4 字节。TCP 连接中传送的数据流中的每一个字节都编上一个序号。序号字段的值则指的是本报文段所发送的数据的第一个字节的序号
序号字段:
现有3000个字节的数据。假设报文段的最大数据长度为1000个字节,初始序号为1001
报文段 1 序号 = 1001 (数据字节序号:1001 ~ 2000)
报文段 2 序号 = 2001 (数据字节序号:2001 ~ 3000)
报文段 3 序号 = 3001 (数据字节序号:3001 ~ 4000)
- 确认号字段——占 4 字节,是期望收到对方的下一个报文段的数据的第一个字节的序号,如果设置了ACK控制位,这个值表示一个准备接收的包的序列码
- 数据偏移(即首部长度)——占 4 位,它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。"数据偏移"以 4 字节为计算单位
- 保留字段——占 6 位,保留为今后使用,但目前应置为 0
- 紧急 URG —— 当 URG = 1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)
- 确认 ACK —— 当 ACK =1 时确认号字段才有效。大多数情况下该标志位是置位的。TCP报头内的确认编号栏内包含的确认编号(w+1)为下一个预期的序列编号,同时提示远端系统已经成功接收所有数据
- 推送 PSH (PuSH) —— 接收 TCP 收到 PSH = 1 的报文段,就尽快地交付接收应用进程,而不再等到整个缓存都填满了后再向上交付复位 RST (ReSeT) —— 当 RST=1 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接
- 同步 SYN —— 同步 SYN = 1 表示这是一个连接请求或连接接受报文
- 终止 FIN (FINish) —— 用来释放一个连接。FIN=1 表明此报文段的发送端的数据已发送完毕,并要求释放运输连接
- 窗口字段 —— 占 2 字节,用来让对方设置发送窗口的依据,单位为字节
- 检验和 —— 占 2 字节。检验和字段检验的范围包括首部和数据这两部分。在计算检验和时,要在 TCP 报文段的前面加上 12 字节的伪首部
- 紧急指针字段 —— 占 16 位,指出在本报文段中紧急数据共有多少个字节(紧急数据放在本报文段数据的最前面)
- 选项字段 —— 长度可变。TCP 最初只规定了一种选项,即最大报文段长度 MSS。MSS 告诉对方 TCP:我的缓存所能接收的报文段的数据字段的最大长度是 MSS 个字节
- 填充字段 —— 这是为了使整个首部长度是 4 字节的整数倍
TCP的连接
TCP的连接是一条点对点的连接:
- TCP 把连接作为最基本的抽象,每一条 TCP 连接有两个端点
- TCP 连接的端点不是主机,不是主机的IP 地址,不是应用进程,也不是运输层的协议端口。TCP 连接的端点叫做套接字 (socket) 或插口
- 端口号拼接到 (contatenated with) IP 地址即构成了套接字
套接字我们认为是TCP连接的一端,有了两个套接字,我们就可以 建立一条TCP连接
TCP可靠传输的工作原理
停止等待协议
TCP协议是一个很复杂的协议,它在面向连接的基础上,通过若干控制,可以达到可靠传输,拥塞控制,连接管理等功能
- 在运输层之下的网络层,IP协议能够做到尽最大努力交付,也就是说IP数据报从一台主机到达另一台主机是可以通过路由来找到一条合适的路径到达对方,但是最后能不能到达对方,中间会不会出错,这个IP协议是不能保证的
- 在IP之上,运输层当中的TCP协议,在连接的基础之上,可以做到在不可靠的通信链路上实现可靠传输
我们可以想象,理想的传输条件应该具有以下两个特点:
- 传输信道不产生差错,也就是传输过程中,数据的比特不会因为干扰出现任何差错
- 不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据
然而实际的网络都不具备以上两个理想条件。必须使用一些可靠传输协议,在不可靠的传输信道实现可靠传输,其中停止等待协议就是其中典型的一个协议
停止等待协议:
- "停止等待"就是每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组,对方确认保证了数据的无差错,由于发送的数据也是按顺序发送的,所有最后数据的顺序也不会出现错乱
- 全双工通信的双方既是发送方也是接收方,但为了讨论方便,这里仅说明其中一方发送,另一方接收的情况
无差错情况
出现差错
在接收方 B 会出现两种情况:
- B 接收 M1 时检测出了差错,就丢弃 M1,其他什么也不做(不通知 A 收到有差错的分组)
- M1 在传输过程中丢失了,这时 B 当然什么都不知道,也什么都不做
M1 在传输过程中丢失了,这时 B 当然什么都不知道,也什么都不做,但A都必须重发分组,直到B正确接收为止,这样才能实现可靠通信,但是此时又出现了如下问题
问题1:A如何知道 B 是否正确收到了 M1 呢?
解决办法:超时重传
- A 为每一个已发送的分组都设置了一个超时计时器
- A 只要在超时计时器到期之前收到了相应的确认,就撤销该超时计时器,继续发送下一个分组 M2
- 若A在超时计时器规定时间内没有收到B的确认,就认为分组错误或丢失,就重发该分组
问题2:若分组正确到达B,但B回送的确认丢失或延迟了,A未收到B的确认,会超时重发。B 可能会收到重复的 M1 。B如何知道收到了重复的分组,需要丢弃呢?
解决办法:编号
- A为每一个发送的分组都进行编号。若B收到了编号相同的分组,则认为收到了重复分组,丢弃重复的分组,并回送确认
- B为发送的确认也进行编号,指示该确认是对哪一个分组的确认
- A根据确认及其编号,可以确定它是对哪一个分组的确认,避免重发发送。若为重复的确认,则将其丢弃
问题3:若B正确收到了A的报文,并发送了确认报文给A,但是这个确认报文丢失了怎么办?
解决办法:超时重传
- 若 B 所发送的对 M1 的确认丢失了,那么 A 在设定的超时重传时间内不能收到确认,但 A 并无法知道:是自己发送的分组出错、丢失了,或者 是 B 发送的确认丢失了。因此 A 在超时计时器到期后就要重传 M1
- 假定 B 又收到了重传的分组 M1。这时 B 应采取两个行动:第一,丢弃这个重复的分组 M1,不向上层交付,第二,向 A 发送确认。不能认为已经发送过确认就不再发送,因为 A 之所以重传 M1 就表示 A 没有收到对 M1 的确认
问题4:若B正确收到了A的报文,并发送了确认报文给A,但是这个确认报文迟到了怎么办?
解决方法:丢弃
- A 会收到重复的确认。对重复的确认的处理很简单:收下后就丢弃
- B 仍然会收到重复的 M1,并且同样要丢弃重复的 M1,并重传确认分组
注意:
- 在发送完一个分组后,必须暂时保留已发送的分组的副本,以备重发
- 分组和确认分组都必须进行编号,超时计时器的重传时间应当比数据在分组传输的平均往返时间更长一些
连续 ARQ 协议
- 通常 A 最终总是可以收到对所有发出的分组的确认。如果 A 不断重传分组但总是收不到确认,就说明通信线路太差,不能进行通信
- 使用上述的确认和重传机制,我们就可以在不可靠的传输网络上实现可靠的通信
- 像上述的这种可靠传输协议常称为自动重传请求 ARQ (Automatic Repeat reQuest)。意思是重传的请求是自动进行的,接收方不需要请求发送方重传某个出错的分组
通过上述描述,我们发现若是每次发送一个数据报,然后等待确认,这样信道利用率是非常低的,如下图所示:
为了提高信道利用率,采用了流水线工作方式,即批量发送数据,然后逐个等待确认,注意每次发送数据的量是有限制的,如下图所示:
基本思想
- 发送方一次可以发出多个分组,使用滑动窗口协议控制发送方和接收方所能发送和接收的分组的数量和编号
滑动窗口:
位于滑动窗口内的分组才能够发送
位于滑动窗口内的分组才能确认接收
每收到一个确认,发送方就把发送窗口向前滑动
接收方一般采用累积确认的方式:即不必对收到的分组逐个发送确认,而是对按序到达的最后一个分组发送确认,这样就表示:到这个分组为止的所有分组都已正确收到了
采用回退N(Go-Back-N)方法进行重传
回退N:
如果发送方发送了前 5 个分组,而中间的第 3 个分组丢失了。这时接收方只能对前两个分组发出确认。发送方无法知道后面三个分组的下落,而只好把后面的三个分组都再重传一次
这就叫做 Go-back-N(回退 N),表示需要再退回来重传已发送过的 N 个分组
TCP的流量控制
- 一般说来,我们总是希望数据传输得更快一些。但如果发送方把数据发送得过快,接收方就可能来不及接收,这就会造成数据的丢失
- 流量控制 (flow control) 就是让发送方的发送速率不要太快,既要让接收方来得及接收,也不要使网络发生拥塞
- 利用滑动窗口机制可以很方便地在 TCP 连接上实现流量控制