美文网首页
网络协议补完计划--TCP协议

网络协议补完计划--TCP协议

作者: kirito_song | 来源:发表于2018-07-11 17:07 被阅读655次

目录

  • TCP协议的基本概念
    • 面向链接的服务
    • 可靠的服务
    • 序列号
    • 字节流传输
  • TCP协议数据段的格式
    • TCP伪头部
  • TCP协议链接的建立和关闭
    • TCP连接的建立
      • 三次握手
      • 序列号和确认号
      • 建立连接时的初始数据
    • TCP连接的关闭
      • 四次挥手
      • 复位
    • TCP连接的状态迁移
      • 三次握手的状态迁移
      • 同时打开的状态迁移
      • 四次挥手的状态迁移
  • TCP协议数据的传送与流量控制
    • 字节流的分段
      • TCP缓冲区
      • TCP数据段的长度
      • TCP选项(字段)
    • 滑动窗口机制
      • 基本滑动窗口机制
      • TCP数据段的标识符-序列号
      • TCP的滑动窗口机制
      • 可变窗口大小
    • TCP的拥塞机制
      • 拥塞窗口
      • 成倍减少机制
      • 慢启动机制
      • 拥塞避免机制
    • 紧急数据的传输
      • 普通紧急数据
      • 带外数据
  • TCP的傻窗口症状
    • 如何解决傻窗口的问题
  • TCP协议与UDP协议的比较
    • 面向连接的传输服务
    • 可靠的传输服务
    • 面向字节流的传输服务
  • TCP应用于UDP应用
    • TCP应用
      • 大文件传输--FTP
      • 远程登录--Telnet
      • 可靠传输--http
    • UDP应用
      • 实时应用
      • 多播式应用
      • 某些时候的降级策略
    • 使用UDP时应该注意
  • QQ关于TCP以及UDP的一些帖子整理
    • QQ既有UDP也有TCP
    • 早期客户端与客户端主要使用UDP直接通讯
    • 有了代理服务器之后的当下
    • 为什么最初选择UDP
    • QQ的TCP长连接到底在做什么、为什么不用已经建立好的TCP却还是用UDP发送消息?
    • 大文件传输/视频下的TCP

前言

参照清华大学出版社-罗军周主编的《TCP/IP协议及网络编程技术》进行学习。
本篇主要参考第九章:《TCP协议》


TCP协议的基本概念

TCP协议为应用软件提供一种面向链接可靠字节流传输服务的协议。

引用《网络协议补完计划--TCP/IP协议概述》中的解释:

亚当和夏娃分别生活在两个山头,山头之间是万丈深渊,亚当采集野果需要分享给夏娃,如果他们之间有一条索道(物理连接),野果可以顺着索道滑到夏娃那一边,那就没有网络协议什么事了。
事实上山头之间没有索道。但是亚当何等聪明,于是他想出了一个方法,假设亚当需要给夏娃10个野果,否则她会饿死。

TCP协议能够确保从发送方发出的数据、按照顺序完整的交付给接收方。

连接建立
亚当对着夏娃大喊:爱妃,你听得到吗?
夏娃回应:孩他爹,我听得到!
亚当接着喊:那好,我扔果子给你吃,你接到果子就喊一声,一共十个。

运送货物
于是亚当开始扔第一个,夏娃喊收到了一个。
亚当扔第二个,夏娃喊收到两个。
超时重传 ( timeout retransmit)
亚当扔第三个,可是夏娃迟迟没有回音,亚当意识到可能果子落到悬崖了,于是重新扔,夏娃喊收到第三个。
Advertised window size = 0
于是亚当连续扔了第四、五、六个,夏娃急了:孩他爹,慢点扔,臣妾忙不过来了…
Advertised window size > 0
于是亚当坐下休息,爱妃又开始叫了:继续扔吧。
亚当开始扔第七个,夏娃喊收到七个。

关闭连接
终于亚当扔完了,亚当喊:爱妃,果子扔完了,寡人去忙别的了。
夏娃回复:好的,我也休息一下,再见。
亚当:再见
以上的过程类似TCP连接的过程,TCP是一个虚拟连接

——————

  • 面向链接的服务

1. 连接状态与各个端(主机)有关、与其间的路由器无关。
2. 链接是点对点双向的、双方都可以发送或接收。
3. 端口只负责接收数据、链接标识符决定最终流向。
与UDP协议不同、TCP协议的端口只负责数据的接收。
最终交给哪个应用程序处理、取决于链接的标识符。
4. TCP协议允许多个TCP连接共用一个端口。
由于上一个原因、即使使用同一个端口。我们依旧可以把数据送达正确的接受者受众
5. 无法使用广播或多播
由于TCP链接是针对两端进行的、所以只能二者之间进行通信。
——————

  • 可靠的服务

我们一直强调IP协议提供的是不可靠的传输服务、既IP数据包可能会丢失、失序、重复等等。
TCP协议作为上层服务是如何通过一个不可靠的传输可靠的呢?

通过超时从发机制来实现
  1. 接收方在收到发送方的数据后需要给发送方回复一个确认
  2. 发送方在发出数据后会等待接收方的确认
  3. 在收到确认之前不会发送下一条数据。
  4. 如果等待一定时间仍没有收到确认就将刚刚的数据重发
  5. 知道收到确认或者重发次数用完为止。
确认--重发示意图

——————

序列号

很多时候并不是数据包丢失、而是由于一些原因导致网络延时、这时候重发数据会导致接收方收到两次数据、发送方也会收到两次确认。
那么如何确认发送的数据以及确认是同一个呢?
就要在数据包中记录一个序列号的字段。
相同的数据会使用同一个序列号、确认信息也会注明是对哪一个序列号的确认。上图中的1、2、3就是不同的序列号

——————

字节流传输

TCP协议向上层应用程序提供的传输服务是面向字节流的。
TCP协议将数据看做有序排列的二进制位、并按照8位分割成有序的字节流。

  • 发送方应用层将数据以字节流的顺序递交给下层TCP协议进行传输
  • 接收方的TCP协议会将数据以相同的字节流顺序交付给接收方应用程序。
  • TCP虽然将数据包一次一次的进行传输、但并不关系数据的具体结构。
  • 接收方可能在一次收取动作中无法获得全部数据、需要将数据包拼接还原。

TCP协议数据段的格式

应用层不会在意字节流是怎样进行传输的、但IP协议的传输是受限于IP数据包大小的(很可能比用户数据总量大但是比TCP数据包小)、因此TCP协议必须将字节流数据进行分割并组成IP数据包进行传输、在目标主机的TCP协议将会将这些数据再组合成数据流。
TCP协议数据包有自己的头部和数据区、一个TCP数据包称为段。

TCP协议数据段

——————

TCP伪头部

作用和UDP协议的伪头部相同、用于计算校验和。


TCP协议链接的建立和关闭

TCP连接的建立

一个TCP连接由四部分组成。发送方的IP地址与端口号、接收方的IP地址与端口号。
建立一个链接是为了让发送方和接收方都做好准备开始传输数据。

——————

主动打开与被动打开

  • 主动方
    向系统申请一个TCP端口、并发出第一个消息(连接请求)的一方

  • 被动方
    已经向系统申请好了一个TCP端口(监听端口)等待其他主机连接请求的一方

  • 三次握手


  1. 三次握手的主要目的在于同步连接双方发送数据的初始序列号。
    连接中每次数据的发送、都需要附带序列号。作用已经在前文中提过
  2. 连接是双向的
    数据的发送/回复是双向的。每个方向都有自己维护的序列号

还需要注意的是三次握手中数据包的代码位

  • SYN - 创建一个连接
  • ACK - 同意一个操作

我们来解释一下上图中每步的含义:

  1. 第一次握手: (主动方 ----->> 被动方)链接待建立

主动方向被动方申请建立连接(SYN)--以发送数据给被动方

序列号seq字段设置为主动方初始序列号 + 代码位设置为syn

  1. 第二次握手: (主动方 ----->> 被动方)链接已建立、 (被动方 ----->> 主动方)链接待建立

被动方同意建立连接(ACK)--以接收主动方数据
被动方申请建立连接(SYN)--以发送数据给主动方

序列号seq字段设置为被动方初始序列号
ack设置成主动方初始序列号+1
代码位设置为syn、ack

  1. 第二次握手: (主动方 ----->> 被动方)链接已建立、 (被动方 ----->> 主动方)链接已建立

主动方同意建立连接(ACK)--以接收被动方数据

ack设置成被动方初始序列号+1

  • 两个主动打开连接建立的过程

这种情况发生在两端几乎同时发送SYN并且这两个SYN在网络中交错的情形。这种情况可能发生,但是非常罕见。

需要注意的是、上图中红色部分以及绿色部分(发送SYN时)、初始序列号字段必须是相同的、否则接收方就不知道它的初始序列号是多少了。

  • 序列号和确认号

TCP会话的每一端都包含一个32位(bit)的序列号,该序列号被用来跟踪该端发送的数据量。每一个包中都包含序列号,在接收端则通过确认号用来通知发送端数据成功接收

当某个主机开启一个TCP会话时,他的初始序列号是随机的,可能是0和4,294,967,295之间的任意值,然而,像Wireshark这种工具,通常显示的都是相对序列号/确认号,而不是实际序列号/确认号,相对序列号/确认号是和TCP会话的初始序列号相关联的。这是很方便的,因为比起真实序列号/确认号,跟踪更小的相对序列号/确认号会相对容易一些

关于为什么需要一个随机的初始序列号
一部分原因是为了防止重复
更重要的原因是在安全上为了防止IP欺骗的攻击
如果确认号固定、那么一旦IP被伪装、即便攻击者接收不到响应包、但还是可以随意建立连接并发送指令(比如注入一些后门指令)。

  • 建立连接时的初始数据

一般建立连接的TCP段中是不带数据的、但不意味着不能携带数据。
有时会我们会在三次握手中将数据区加入数据、接收方将其保存下来、在建立连接之后就可以迅速递交给上层应用了。

——————

TCP连接的关闭

  • 四次挥手
  1. TCP连接是双向的
    可以看做两个管道、同一时刻每一个管道都可以发送数据
  2. 半关闭
    只关闭一个方向的数据传输、另一个方向还是可以继续的

同样需要注意的是三次握手中数据包的代码位:

  • FIN - 终结一个连接
  • ACK - 同意一个操作
  1. 主动方申请关闭
    连接的主动方先发送FIN数据段、告诉被动方自己的数据发送完了。
  2. 被动方确认
    连接的被动方很可能还没发送完数据、所以只能用ACK段告诉主动方自己知道也同意主动方关闭管道不在发送数据过来。
  3. 被动方申请关闭
    连接的被动方在确认自己的数据也发送完了之后、发送FIN段给主动方关闭连接。
  4. 主动方确认
    连接的主动方通过ACK告诉被动方、同意被动方也关闭管道。

需要注意的是在四次挥手的过程中、不需要发送序列号了(至少我没找到??)。

  • 复位
    涉及到另一个代码位:RST - 复位
    直接将连接关闭、不需要四次挥手。直接关闭双向连接

——————

TCP连接的状态迁移

  • 三次握手的状态迁移


  • 同时打开的状态迁移


  • 四次挥手的状态迁移


  • 计时等待状态(2MSL)

每个TCP具体实现、必须选择一个TCP段最大生存时间MSL
也就是每一个TCP段被丢弃前在网络内的最长时间。

时长通常为2min

当一个TCP连接执行主动关闭、并发回最后一个ACK(确认)、该连接必须在计时等待状态停留2倍的MSL。
这样可以让TCP有一次重发这个ACK的机会。

这种设计的另一个结果就是这个TCP连接在2MSL的等待时间内、定义这个链接的端口不能再被使用。


TCP协议数据的传送与流量控制

主要涉及字节流的分段、TCP的确认重发机制、超时的判断及紧急数据的传输等

字节流的分段

由于TCP上应用层提供的是字节流的传输服务。为了避免用户提供少量多次的数据导致传输效率低下、TCP协议采取的是缓冲的办法。

  • TCP缓冲区

应用层提供给TCP协议的数据会被先放入缓冲区中、并没有真正的发送。只有在合适的时候或者应用程序显示地要求将数据发送时、TCP才会将数据组织成合适的数据段发送出去。
至于TCP协议的缓冲区有多大、一般要根据TCP协议确定的最大段长度决定。

  • TCP数据段的长度

TCP数据段最终将会封装IP数据包中进行发送。
从理论上讲、TCP数据段的最大长度加上IP数据包头部长度不能超过65535个8位字。
但实际上、TCP数据段的长度要远远小于这个值。主要有一下几个原因:

  1. IP数据包的分片
    对于不同的物理链路限制、IP数据包可能会分片、这在一定程度上会影响传输效率。
  2. IP数据分片的丢失与重发
    而分片就意味着重组、如果缺少一个分片、那么整个IP数据包都将面临重发。
  3. 传输两端主机系统的计算能力差别
    大型服务器的内存数量和普通主机存在十分大的差异、必须迁就着弱势一方。以免弱势一方被一个超大的数据包淹没。

基于以上两个原因、TCP数据包的大小应该被控制在合理的范围、使其最终能够封装在IP数据包中通过一个数据帧进行传输。

  • TCP选项(字段)

建立连接时TCP协议数据段中、有一个TCP选项的字段。

TCP选项格式

其中:

  1. 类型字段: 长度为8位。 2标识最大段长
  2. 长度字段: 长度为8位。
  3. 选项数据: 长度为16位。
  • MSS与MTU

MSS(最大报文段长度)我们可以通过TCP报文的头部TCP选项字段得到。
MTU(最大传输单元)针对不同的物理链路会有不同的约束、而且这种下层的约束是上层协议无法获取的。

——————

滑动窗口机制

单纯的用每个数据段发送确认最后重传的机制、传输时间会成倍增长、很不效率。

实际上TCP协议使用的确认重发机制是一种称为滑动窗口的机制。
他可以让发送方连续发送多个数据段、然后再等待接收方确认

  • 基本滑动窗口机制

TCP协议发送的字节流根据状态可以分为三类:

  1. 已发送、已确认(发送成功)
    数据确定已经被接收方获取

TCP协议可以完全不用关心、也不保存

  1. 已发送、待确认(发送中)
    数据可能还在传输中/数据已经被接收但确认信息在传输中

为数据段启动一个定时器
定时器超时没有收到确认时、对该数据段执行重发动作。

  1. 未发送
    数据还没有被发送

可能应用程序还没有提交给TCP协议。
也可能以字节流的形式保存在TCP的缓冲区中

滑动窗口机制-1

如上图所示、在数据不断发送的过程中。蓝色的窗口会不断的向右移动。知道所有数据全部变成绿色(已发送)。

在滑动窗口机制中、窗口的大小(允许同时发送的数据量)决定了数据传输的效率。在极端情况下可能为1。

滑动窗口的移动
  1. 滑动窗口中的数据并不是同时发送、而是有先后(数据的准备时间)的。
  2. 当收到一个第一个确认后、将窗口向右移动一个。发送第五个数据段。

如果窗口大小控制得当(第四个数据段发出刚好收到第一个确认)、可以极大程度的利用TCP这种全双工连接的的特点以同时进行双向传输。

  • TCP数据段的标识符-序列号

防止数据包失序、以及在重发机制下用来区分接收到的数据是否是同一个的特殊号码

  1. 序列号不是数据段的编号
    是按字节进行编号的。比如一个数据段的序列号是216且该数据段携带了100B数据。那么下一个数据段的序列号就是216+100=316。

  2. 确认号字段也不是确认段的编号
    确认号也是按字节进行编号的。比如一个他接收到的数据段序列号是216且携带了100B数据。那么他发送的确认信息中确认号则应该为216+100=316。表示他希望接收序列号为316的数据段。
    需要注意的是确认号并不是在每个数据段中都用到、只有在数据段中设置了ACK位、才会识别确认号。

  3. 三次握手时的序列号以及确认号

    三次握手中序列号的变化

先介绍其中字段的含义
seq:序列号
aseq:确认号
dlen:数据区长度。建立连接时通常不携带数据、但是不代表不能携带。

上图更加直观的印证了

  1. 确认号=序列号+数据区长度
    但是在连接的建立阶段需要额外+1、不然由于数据区为空。序列号永远是一个了。
  2. 双方各自维护各自的序列号
    主动方序列号x与被动方序列号y。没有丝毫的联系
  • TCP的滑动窗口机制

之前的例子是以数据段为单位进行解释、但实际工程中的滑动窗口、是以字节作为基本单位工作的。

发送方窗口
在TCP协议中、发送方每个窗口都用三个指针表示

发送方滑动窗口指针
  1. 第一个指针
    指向窗口范围的第一个字节
  2. 第二个指针
    指向马上要发送的字节
  3. 第三个字节
    指向窗口中的最后一个字节

接收方窗口
在TCP协议中、接收方窗口用三个指针并不能完全表示

接收方滑动窗口
  1. 实线部分(201到1000
    当前窗口
  2. 白色部分(201到500以及733到800
    等待接收数据的部分
  3. 深色部分(501到732以及801到1000
    已经接收到信息的部分

需要注意的是确认信息中的确认号、代表对于接收方已经将确认号前的数据全部接收完毕。

那么、当接收方只接收到501到732的数据段却没有接收到201到500的数据段时、接收方会将这段数据暂存起来。
之后、如果接收到一个序列号为201、数据长度为300的数据段。
将会发送确认号为733的数据段。
那么接收方的窗口将移动到733(当然发送方也是)。

由于TCP链接是全双工的、所以发送方以及接收方每一端都必须保存两个窗口。一个负责发送、一个负责接收。
这种确认方式我们称之为《累计确认》

关于累计确认的优点:

1. 他允许发送方在重发时发送比之前正常发送时还要多一些的数据
比如最初发送了500B的数据、在确认回来之前客户端又交付了300B。这时他就可以将800B的数据一次发送出去。变相的增加了之前500B到达的几率。
2. 确认累计无需对丢失的确认进行重发
由于接收方发送的确认号会直接更改发送方的窗口位置(参考刚才对于接收方窗口的解释)、所以哪怕是确认丢失也无需重发、只要下一个确认能够正确到达就可以。

关于累计确认的缺点:

** 一个极端的情况**


累计确认的极端情况

如图所示、后面的三个数据段全部送达、但是由于第一个数据段丢失。接收方无法回复确认号、导致发送方的窗口第一个序列号依旧为201。

这时发送方不知道该发送第一个数据段还是所有数据段。如果每次都重发所有数据段、可能会对网络造成很大的负担。

针对这个情况、目前的标准做法是:

  1. 重发第一个数据段、通过确认号来判断刚才的几个数据段成功了几个。
  2. 如果几次发送之后确定接收方刚才的四个数据段一个都没有收到、那么则重回单个数据包的简单发送模式。
  • 可变窗口大小

由于缓冲区的存在、维护接收窗口是需要资源的。而一些性能较差的主机系统而言维护并不能同时处理那么多的数据。
所以、我们要求发送方的窗口不能大于接收方的窗口(通常要小一些)。

接收方窗口的大小、则在发送每一个ACK设置为1确认包时、通过窗口字段来告知发送方。这样做、也就允许接收方随时改变窗口的大小。

需要注意的是。在缩小窗口时、新窗口的右边界不能小于原窗口的右边界、不过扩大是没有问题的。
对于缩小窗口、极限值为0。也就以为这停止传输。

借用书上的例子

TCP接受窗口大小变化示意图
  • 上面的例子中前三行窗口越来越小
    上层应用可能由于很多原因(比如数据发送过快、接收方来不及处理需要人工干预、正在等待处理等等)无法接收数据、导致TCP缓冲区的数据无法提交给应用层
    这无形中就减小了缓冲区的大小、使得窗口的右边界无法向右移动

  • 发送方窗口也将相应变小
    接收方将变小的接受窗口通过ACK数据段发送给发送方、发送方也会相应的减小(如果需要)发送窗口的大小以配合接收方。

  • 窗口复原
    最后一行、上层应用程序处理完数据。TCP可以将缓冲区中积压的数据传递上去、窗口也就可以恢复原样了。

综上所述

TCP协议使用的是。以《《累计确认》》为基础、以《《字节为单位》》《《滑动窗口机制》》。实现了数据的可靠、高效传输。还实现了流量控制(处理拥塞)。

————————

TCP的拥塞机制

所谓的拥塞就是只网络中的转发设备(路由器)因为过多的数据包需要转发而造成某些数据包的延迟或者丢失。
一旦路由器的存储资源被占满、后到达的数据包就将被丢弃。
路由器丢弃数据包时、会向源主机发送类型为源端关闭的ICMP数据包(关于ICMP可以参考《网络协议补完计划--ICMP协议》)。
但需要注意的是ICMP协议并不能解决问题、因为数据包一旦超时或丢失、IP协议(只负责运输数据)通常的反应就是重新发送、久而久之造成网络瘫痪。

因此、需要上层(TCP)协议对网络状态进行判断并且做出正确的反应。《《滑动窗口机制》》的另一个作用就是减轻或避免网络拥塞。
而TCP专门负责处理网络拥塞的机制称为《慢启动》《成倍减少》、二者相辅相成。

  • 拥塞窗口

之前我们提到发送方的窗口不能大于接收方窗口
这里还有另一个限制、就是拥塞窗口。
实际的发送窗口状态将会二者取其小。

  • 成倍减少机制

当拥塞发生。通过减小拥塞窗口的大小来减少发送的数据包数量。

当TCP协议发现数据包丢失(即将超时重发)时、具体的操作是:

  1. 减少窗口
    将拥塞窗口减小一半(但至少为1)。
  2. 增加超时间隔
    并且将仍在其中的数据段重发时间增加一倍。

于是、如果数据包不断丢失、TCP的发送窗口和重发时间都会以指数级变化。
最终、发送的数据段将减小到1、且重发间隔不断增加。以达到帮助路由器缓解拥塞的作用。

  • 慢启动机制

当路由器从拥塞状态恢复之后、TCP恢复到正常发送窗口的过程。

TCP不会立即将发送窗口恢复到与接收窗口匹配的状态、以防再次造成拥塞。

具体机制是这样的:
TCP在《新启动一个连接》或者《拥塞结束》时、将拥塞窗口的大小设置为一个数据段的大小。
然后每收到一个确认段、TCP就将拥塞窗口的大小增加一倍数据段

虽然称为慢启动、但是如果网络畅通、窗口将会以指数级增加、该机制并不会影响传输的速度。

  • 拥塞避免机制

为了防止慢启动时窗口扩大过快、TCP还有这样一个机制:
《在拥塞发生并恢复后》、一旦通过慢启动机制使得拥塞窗口的大小达到了拥塞之前的一半
TCP将会启动一个拥塞避免机制以减缓窗口扩大的速度。
在该机制下、只有当前窗口所有的数据确认后、才增加一个数据段大小。

————————

紧急数据的传输

紧急数据是指发送是不经过缓冲区的拼接。应用层要求直接发送到接收方的数据。

紧急数据分为以下两种:

  • 普通紧急数据

数据虽然会马上发送给另一端、但是还需要对方按照字节流的顺序进行处理、因此需要先将前面的数据发送或处理后才能发送该紧急数据。

比如主机A的输入框输入了几个字符、这些字符需要马上发送给主机B。但是几个字符的数据量不足以组成一个TCP数据段。因此需要用这个机制将用户指定的数据马上发送出去。但主机B处理的时候依旧是按照接收顺序来处理的。

  • 带外数据

数据也会马上发送给另一端、同时需要接收方马上处理(而不管接收时的字节序)。

也就是所带外数据存在于另一个数据流中、比普通数据的优先级更高。

————————

超时的判断

确认重发机制要求发送方在发送数据后启动一个计时器、在计时器超时后就假设数据丢失并重发。


TCP的傻窗口症状

由于接收方的处理速度过慢、导致接收方窗口很小(比如1B)。
进而导致发送方每次发送数据也很小。浪费了带宽加重了发送方和接收方底层协议的负担

  • 如何解决傻窗口的问题呢?

对于接收方--防止发送小窗口

在发送过0大小的窗口确认通知后。
如果缓存区没有扩大到足够的大小、则延迟发出确认通知。
等待应用程序处理一定量的数据知道腾出足够大小的缓冲区(最大可用缓冲区大小的一半/最大数据段二者中较小的值)、再发送新的确认通知扩大窗口。

不过延迟确认发送也有自己的缺点。

  1. 可能会造成无用的重发
  2. 由于TCP需要通过确认到达的时间来估算RTT(数据来回时间)、因此延迟确认会增大RTT最终导致对丢失的数据重发等待的时间太长。

于是、TCP规定数据确认的延迟不能超过500ms。另外、为了让发送方能够收集足够的RTT样本、TCP规定对其他情况的数据段必须逐一进行确认。

对于发送方--防止在数据段中每次只发送小字节

利用缓冲区的机制实现、当应用程序提交了一个数据交给TCP协议发送、TCP都会现将该数据缓存。而后分为两种情况:

  1. 如果TCP正在等待之前的确认信息
    直到能够组成一个最大数据段时再发送。
  2. 如果收到了一个确认信息
    不在等待、直接发送。

以上的策略、对push操作也适用。
这个策略被称为Nagle算法。几乎不需要发送方为该算法进行额外的计算、也不需要计时器这种资源。而且对各种应用都适用、不会影响他们的吞吐量和反应速度。


TCP协议与UDP协议的比较

面向连接的传输服务

  • TCP以连接作为协议数据的最终目标
    TCP协议的端口是可以复用

对于TCP协议,要成功建立一个新的链接,需要保证新链接四个要素组合体的唯一性:客户端的IP、客户端的port、服务器端的IP、服务器端的port。也就是说,服务器端的同一个IP和port,可以和同一个客户端的多个不同端口成功建立多个TCP链接(与多个不同的客户端当然也可以),只要保证【Server IP + Server Port + Client IP + Client Port】这个组合唯一不重复即可。

  • UDP以端口作为协议数据的最终目标
    UDP协议的端口不可不用

对于UDP协议、是以监听端口作为操作的。而且在协议中、源端口和源IP地址都是可选项。哪怕不填(只制定了目的端口和目的地址)也可以成功发送。

  • TCP协议需要先建立连接、然后才能发送/接收数据
    并且需要对很多细节进行协商(最大数据长度、窗口大小、初始序列号等)
  • UDP协议直接发送/接收数据

可靠的传输服务

  • TCP协议提供的是可靠的传输服务
    以序列号保证有序、以重发机制保证成功发送。
  • UDP协议提供的是不可靠的传输服务
    可能会丢失、失序、重复等。

面向字节流的传输服务

  • TCP协议是以字节为单位流式传输数据
    TCP的传输是无边界的

TCP通过字节流传输,即TCP将应用程序看成是一连串的无结构的字节流。每个TCP套接口有一个发送缓冲区,如果字节流太长时,TCP会将其拆分进行发送。当字节流太短时,TCP会等待缓冲区中的字节流达到一定程度时再构成报文发送出去。

  • UDP协议是以数据块传输数据
    UDP的传输是有边界的

而UDP传输报文的方式是由应用程序控制的,应用层交给UDP多长的报文,UDP照样发送,既不拆分,也不合并,而是保留这些报文的边界,即一次发送一个报文。


TCP应用于UDP应用

  • TCP应用

大文件传输--FTP

FTP是TCP/IP协议族之一、属于依赖TCP的应用层协议。
适用于大批量、高可靠性要求的应用

远程登录--Telnet

适用于小批量、长时间、高可靠性要求的应用

可靠传输--http
  • UDP应用

实时应用

TCP的传输时有先后顺序的、所以有时会阻塞

多播式应用

TCP是面向连接的、如果要多播需要建立n*(n-1)个连接。

某些时候的降级策略

引用美团移动网络优化策略里的一段话:

当TCP通道无法建立或者发生故障时,可以使用UDP面向无连接的特性提供另一条请求通道,或者绕过代理长连服务器之间向业务服务器发起HTTP公网请求。

  • 使用UDP时应该注意一下几点:

1. 应用程序必须自己来保证可靠性
应用程序必须有自己的重发机制、数据失序处理、流量控制等。
2. 应用程序必须自己来处理大块数据
发送方对大块数据进行分割、接收方还要进行重组


QQ关于TCP以及UDP的一些帖子整理

(需要声明的是我找到的都是一两年前的贴、现在QQ爸爸技术进步实在是太快、没准哪天就改了)

  • QQ既有UDP也有TCP

不管UDP还是TCP,最终登陆成功之后,QQ都会有一个TCP连接来保持在线状态。这个TCP连接的远程端口一般是80,采用UDP方式登陆的时候,端口是8000。

  • 早期客户端与客户端主要使用UDP直接通讯

QQ客户端之间的消息传送也采用了UDP模式,因为国内的网络环境非常复杂,而且很多用户采用的方式是通过代理服务器共享一条线路上网的方式,在这些复杂的情况下,客户端之间能彼此建立起来TCP连接的概率较小,严重影响传送信息的效率。而UDP包能够穿透大部分的代理服务器,因此QQ选择了UDP作为客户之间的主要通信协议。

  • 有了代理服务器之后的当下

现在采用UDP协议,通过服务器中转方式。因此,现在的IP侦探在你仅仅跟对方发送聊天消息的时候是无法获取到IP的。大家都知道,UDP 协议是不可靠协议,它只管发送,不管对方是否收到的,但它的传输很高效。但是,作为聊天软件,怎么可以采用这样的不可靠方式来传输消息呢?于是,腾讯采用了上层协议来保证可靠传输:如果客户端使用UDP协议发出消息后,服务器收到该包,需要使用UDP协议发回一个应答包。如此来保证消息可以无遗漏传输。之所以会发生在客户端明明看到“消息发送失败”但对方又收到了这个消息的情况,就是因为客户端发出的消息服务器已经收到并转发成功,但客户端由于网络原因没有收到服务器的应答包引起的。

  • 为什么最初选择UDP

某次架构师大会上那个58同城做即时通信的人说:原因是因为当时没有epoll这种可以支持成千上万tcp并发连接的技术,所以他们使用了udp,然后在udp上面封装了一下,模拟了一下tcp,解决了大并发的问题,之后因为做的很nb了,虽然epoll这种技术出现了,还是没有改回使用tcp了.现在再做类似的东西就不需要使用udp了.

  • QQ的TCP长连接到底在做什么、为什么不用已经建立好的TCP却还是用UDP发送消息?

分析一下QQ你就会发现,QQ登录时会发送请求给服务器,QQ的服务器并不是一直监听客户端,反之是通过30秒的一个时间间隔来确定客户端是否还在,这个主要的以客户端完成,30秒以内客户端会和服务器握手,告诉服务器我还在,状态正常,而服务器会记录下这个时间,如果30后再次收到客户端握手则维护当前状态,如果收不到则表示客户端掉线了。然后服务器清除当前客户端的状态和注销用户。这就是为什么如果我们是退出QQ,对方很快就看到我们下线了,不超过五秒,但是如果我们的网络出现故障意外断线了,对方要看到你下线需要在大约30-40秒之后。所以使用UDP是为了降低服务器的压力,

  • 大文件传输/视频下的TCP

QQ在文件传输和媒体通讯过程中使用UDP降低了很大的服务器承载(《《《关于这个问题、有人也说使用的是UDP直接在两个客户端之间打洞》》》)。我自己也做IM,早期的版本全跑TCP,用户承载没问题,但是当有文件传输时 服务器承载能力大打折扣,更关键的是多人视频,使用UDP点对点之后客户端内存增加了,CPU也增加了,但是服务器承载变强了。在使用TCP时 ,200人同时视频通话 服务器程序就吃掉970MB内存,2GB的服务器 只能承载200人就差不多极限,8GB内存 4核CPU的服务器也只能承载500-800人的视频通话,改成UDP之后差不多只占原来服务器资源的十五分之一。但是客户端内存消耗增加,使用服务器中转时,客户端9个视频,内存消耗大约90MB,UDP之后这个内存飙升到170MB。


参考资料

读懂TCP状态转移
理解TCP序列号(Sequence Number)和确认号(Acknowledgment Number)
浅析TCP之头部可选项
TCP server 为什么一个端口可以建立多个连接?
美团点评移动网络优化实践
困惑的QQ:既然有了TCP长连接为什么还要用UDP来中转数据呢?
为什么QQ用的是UDP协议而不是TCP协议?

相关文章

网友评论

      本文标题:网络协议补完计划--TCP协议

      本文链接:https://www.haomeiwen.com/subject/etyvuftx.html