TCP 和 UDP 对于软件开发工程师来说并不陌生,学过计算机网络或对 TCP 协议稍有了解的人都懂得如何运用它们编写出各种程序。即使不懂协议具体实现的细节,也不妨碍各种框架帮助你去实现高性能的HTTP请求。

但是我们理应对协议有初步的理解。

在这里作者并不会事无巨细地对协议的每个实现操作或状态转换进行分析,而是会抽离出重要的部分加以分析和讲解。

IP/TCP

TCP 是一个复杂的,可靠的字节流协议。绝大多数网络连接的建立都是基于 TCP 协议的。

TCP 称之为可靠的协议,不代表它发送的数据包百分之百会被目标服务所接收到,它提供的是数据的可靠递送或故障的可靠通知。

三次握手

TCP 在建立连接时会互相确认彼此的状态,这个确认状态的过程我们称之为 “三次握手”。

首先我们默认服务端已经被动打开。

1.客户主动发起连接,它告诉服务器客户将在(待建立的)连接中发送数据的初始序列号。(SYN分节不携带数据,只包含部分TCP选项)

2.服务端确认客户的SYN分节,同时服务端发送一个SYN分节,这个分节内包含服务器将在同一连接中发送的数据初始序列号。

3.客户必须确认服务器的SYN。

BfxmTA.png

序列号

三次握手时,SYN分节中会发送一个初始序列号,这个序列号在TCP中非常重要。客户与服务端在得到各自期望的序列号后完成握手连接。

对于较大的数据包,TCP会对数据包进行切分。为了保证数据包抵达目标位置,TCP会对每个数据包的序列号进行确认,它会对重复序列号的包去重,重复发送未确认的包,对包数据进行排序。

假设客户端要将2048字节到一个TCP套接字,这将使TCP发送两个分节:第一节包含序列号为 11024,第二节序列号包含为 10252048。接受端的TCP会序列号排序,再把数据传递给应用。

在这里我们可以稍稍思考一下,为什么TCP建立连接需要三次,而不是两次或四次。

使用三次握手的主要原因:是为了将是否建立连接的最终控制权交给发送方,由发送方判断当前的连接是否错误或过期。

流量控制和拥塞控制

TCP总是告诉对端它一次能从对端接受多少字节的数据,这称之为 通告窗口。任何时刻,该窗口指出接受缓存区中当前可用的空间量,确保发送端发送的数据不会使接受缓存区溢出。这个窗口时实变化:当接受到来自发送端的数据时,窗口大小就减小。当接收端应用从缓冲区读取数据时,窗口大小就增大。

如果窗口大小减小到0,代表TCP对应某个套接字的接受缓存区已满,那么它必须等待应用从缓存区读取数据,才能再接受数据。

每一个TCP连接都会有一个拥塞控制的窗口,它决定了发送方同时能向接收方发送多少数据。

1.防止发送方向接收方发送了太多数据,导致接收方无法处理。

2.防止双方传输数据过大,导致网络阻塞造成崩溃。

拥塞控制:拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况;常用的方法就是:( 1 )慢开始、拥塞避免( 2 )快重传、快恢复。

流量控制:流量控制是作用于接收者的,它是控制发送者的发送速度从而使接收者来得及接收,防止分组丢失的。

四次挥手

TCP建立连接需要三次握手,终止连接则需要进行四步操作。

1.客户执行主动关闭,同时向服务发送一个FIN分节。

2.收到FIN分节的端会执行被动关闭,它会将一个文件结束符传递给接收端应用进程,这代表接收端应用程序在这个连接上再无额外数据可接收。

3.一段时间后,这个服务端应用程序会关闭它的套接字。同时它的TCP会发送一个FIN分节。

4.原发送端(执行主动关闭的那端)会确认这个FIN 分节。

TIMNE_WAIT 状态

TCP 每一个连接都会有一个默认的 MSL(maximum segment lifttime) 时间,这个时间最短为 30S , 最长则为 120S。MLS是任何IP数据报能在因特网存活的最长时间。

我们可以假设:分组在网络中存在的时间不可能超过 MSL 秒。

TIME_WAIT状态有两个存在的理由:

1.可靠地实现TCP全双工连接的终止。

2.允许老的重复分节在网络中消逝。

TCP 必须防止来自某个连接 旧的重复分组在该连接已终止后再现,从而被误解成属于同一连接的某个新的化身。为了防止这一点,TCP 将不给处于 TIME_WAIT状态的连接发起新化身。TIME_WAIT 状态的持续时间为 MSL的两倍,这让某个方向上的分组最多存活MSL秒就被丢弃,而另外一方向的应答最多存活MSL秒也会被放弃,这足以让旧的重复分节消逝在网络中了。

UDP

UDP 是一个简单,不稳定的传输协议。

它不保证数据报文会到达目的地,也不保证报文的发送顺序,也不保证报文只到达一次。

如果想要使用UDP确保一个数据报到达其目的地,可以在应用程序中添加稳定特性:来自对端的确认,本端的超时与重传等。

UDP提供无连接的服务,它可以创建一个套接字并发送一个数据报给某个服务器,然后立即用同一个套接字发送另一个数据报给另一个服务器。

它和TCP不同,它是多对多的。

我们从上述内容可以看出TCP 的工作方式十分的复杂,它需要保证可靠性,所以不得不使用一系列机制来保证它的高可用和可靠。但是在某种情况下,我们不使用TCP也能高效重发数据。

这种情况需要数据很短,只用一个包就能装下。

UDP 没有 TCP的接收确认,窗口等机制,在收发数据之前也不需要交换任何信息,只要在从应用程序获取的数据前面加上 UDP 头部,然后交给 IP进行发送就行。接收也是一样的,只要根据 IP 头部中的接收方和发送方 IP地址,以及 UDP头部中的接收方和发送方端口号,找到相应的套接字并将数据交给相应的程序就好了。

UDP 只是单纯负责发送包而已,遇到错误或丢包也不会处理。但是这样并不会引发什么问题,只是出错时就收不到来自回复的包,确认包回复或补发包可以在应用程序员中去实现。

UDP 可发送的数据最大长度为 IP 包的最大长度减去 IP 头部和 UDP 头部的长度。

IP 包最大长度为 65535 字节。一般来说 IP 头部为 20 字节,UDP头部为 8字节。

学习资料

《网络是怎么连接的》

《UNIX网络编程卷1:套接字联网API》

为什么TCP需要三次握手

为什么 DNS 使用 UDP 协议