网络基础之UDP与TCP
UDP协议(用户数据报协议):
UDP协议段格式:
- 16位源端口号:它(连同源主机 IP 地址)标识源主机的一个应用进程。。
- 16位目的端口号:它(连同目的主机 IP 地址)标识目的主机的一个应用进程。
- 16位UDP长度 :(也就是一个UDP能传输的数据最大长度)为64kb= UDP头部长度(8字节) + 数据长度.(即2的16次方除1024 = 64kb)
- 16位校验和:用来判断数据的正确性(数据在传输过程中是否被篡改);如果校验和出错,数据就会直接丢弃。
注:
这里有一个面试题: 问 : 如果UDP编程时的数据大小大于64kb会怎样?
有两个解决方案:
- 需要在应用层手动分包,多次发送,并在接收端手动拼接。
- 交给下层(网络层)去处理,他会在网络层进行分包和组包。(但这个方法已经不用了,因为一旦有一个包丢失,就会舍弃所有包;即数据可能丢失,不稳定)
UDP的特点:
无连接: 知道对端的IP和端口号就直接进行传输, 不需要建立连接。
不可靠: 没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层返回任何错误信息。
面向数据报: 不能够灵活的控制读写数据的次数和数量(应用层交给UDP多长的报文,UDP原样发送,既不会拆分,也不会合并。)。
只有接收缓冲区,没有发送缓冲区。( 但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致; 如果缓冲区满了, 再到达的UDP数据就会被丢弃;)
TCP协议(传输控制协议):
人如其名,要对数据的传输进行一个详细的控制。
TCP的特点:
- 有连接。
- 可靠。
- 面向字节流。
- 既有接收缓冲区,又有发送缓冲区。
TCP协议段格式:
- 16位源端口号:它(连同源主机 IP 地址)标识源主机的一个应用进程。。
- 16位目的端口号:它(连同目的主机 IP 地址)标识目的主机的一个应用进程。
- 32位顺序号 seq: 用来标识从 TCP 源端向 TCP 目的端发送的数据字节流,它表示在这个报文段中的第一个数据字节的顺序号。如果将字节流看作在两个应用程序间的单向流动,则TCP 用顺序号对每个字节进行计数。序号是 32bit 的无符号数, 序号到达 2 的 32 次方 - 1 后又从 0 开始。 当建立一个新的连接时, SYN 标志变 1 ,顺序号字段包含由这个主机选择的该连接的初始顺序号 ISN (Initial Sequence Number )。
- 32位确认号 ack: 包含发送确认的一端所期望收到的下一个顺序号。因此,确认序号应当是上次已成功收到数据字节顺序号加 1 。 只有 ACK 标志为 1 时确认序号字段才有效。 TCP 为应用层提供全双工服务,这意味数据能在两个方向上独立地进行传输。因此,连接的每一端必须保持每个方向上的传输数据顺序号。
- 4位TCP 报头长度:给出报头中 32bit 字的数目, 它实际上指明数据从哪里开始。 需要这个值是因为任选字段的长度是可变的。这个字段占 4bit ,因此 TCP 最多有 60 字节的首部。然而,没有任选字段,正常的长度是 20 字节。
- 保留位(6 位):保留给将来使用,目前必须置为 0 。
- 控制位(control flags , 6 位):在 TCP 报头中有 6 个标志比特,它们中的多个可同时被设置为 1 。依次为:
URG :为 1 表示紧急指针有效,为 0 则忽略紧急指针值。
ACK :为 1 表示确认号有效,为 0 表示报文中不包含确认信息,忽略确认号字段。
PSH :为 1 表示是带有 PUSH 标志的数据, 指示接收方应该尽快将这个报文段交给应用而不用等待缓冲区装满。
RST (复位标识): 用于复位由于主机崩溃或其他原因而出现错误的连接。它还可以用于拒绝非法的报文段和拒绝连接请求。一般情况下,如果收到一个 RST 为 1 的报文,那么一定发生了某些问题。
SYN (同步序列号标识):同步序号, 为 1表示连接请求,用于建立连接和使顺序号同步(synchronize )。
FIN(结束序列号标识): 用于释放连接,为 1表示发送方已经没有数发送了,即关闭本方数据流。
- 窗口大小( 16 位):数据字节数,表示从确认号开始,本报文的源方可以接收的字节数,即源
方接收窗口大小。窗口大小是一个 16bit 字段,因而窗口大小最大为 65535 字节。 - 校验和( 16 位):此校验和是对整个的 TCP 报文段,包括 TCP 头部和 TCP 数据,以 16 位字进行计算所得。这是一个强制性的字段,一定是由发送端计算和存储,并由接收端进行验证。
- 紧急指针( 16 位):只有当 URG 标志置 1 时紧急指针才有效。TCP 的紧急方式是发送端向另一端发送紧急数据的一种方式;
- 选项:最常见的可选字段是最长报文大小,又称为 MSS(Maximum Segment Size) 。每个连接方通常都在通信的第一个报文段(为建立连接而设置 SYN 标志的那个段)中指明这个选项,它指明本端所能接收的最大长度的报文段。选项长度不一定是 32 位字的整数倍,所以要加填充位,使得报头长度成为整字数。
- 数据: TCP 报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有 TCP 首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情况中,也会发送不带任何数据的报文段。
全双工: 发送端或者接收端既能发送消息也能接受消息。
TCP八大特性:
1. 确认应答(ACK)机制:
( 保障TCP稳定的核心机制)
TCP将每个字节的数据都进行了编号. 即为序列号;每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据; 下一次你从哪里开始发。
2. 超时重传机制:
当 TCP 发出一个段后, 它启动一个定时器, 等待目的端确认收到这个报文段。 如果不能及时收到一个确认, 将重发这个报文段。
注:
TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间。
- Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控 制, 每次判定超时重发的超时时间都是500ms的整数倍。
- 如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传。
- 如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增。
- 累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接。
3. 连接管理机制:
(在正常情况下, TCP要经过三次握手建立连接, 四次挥手断开连接。)
三次握手过程:
第一次握手: 主机 A 发送位码为 syn=1,随机产生 seq number=1234567 的数据包到服务器,主机 B由 SYN=1 知道, A 要求建立联机;
第 二 次 握 手 : 主 机 B 收 到 请 求 后 要 确 认 联 机 信 息 , 向 A 发 送 ack number=( 主 机 A 的seq+1),syn=1,ack=1,随机产生 seq=7654321 的包
第三次握手: 主机 A 收到后检查 ack number 是否正确,即第一次发送的 seq number+1,以及位码ack 是否为 1,若正确, 主机 A 会再发送 ack number=(主机 B 的 seq+1),ack=1,主机 B 收到后确认seq 值与 ack=1 则连接建立成功。
注:
为什么不是两次握手?
因为两次握手无法证明服务器端的发送能力和客户端的接受能力。
四次挥手过程:
TCP 建立连接要进行三次握手,而断开连接要进行四次。这是由于 TCP 的半关闭造成的。因为 TCP连接是全双工的(即数据可在两个方向上同时传递)所以进行关闭时每个方向上都要单独进行关闭。这个单方向的关闭就叫半关闭。当一方完成它的数据发送任务,就发送一个 FIN 来向另一方通告将要终止这个方向的连接。
- 关闭客户端到服务器的连接:首先客户端 A 发送一个 FIN,用来关闭客户到服务器的数据传送,然后等待服务器的确认。其中终止标志位 FIN=1,序列号 seq=u
- 服务器收到这个 FIN,它发回一个 ACK,确认号 ack 为收到的序号加 1。
- 关闭服务器到客户端的连接:也是发送一个 FIN 给客户端。
- 客户段收到 FIN 后,并发回一个 ACK 报文确认,并将确认序号 seq 设置为收到序号加 1。
首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
注:
- 客户端状态由TINE-WITE转为CLOSED需 要等待2MSL。
为什么是2MSL?
- MSL是TCP报文的最大生存时间, 因此TIME_WAIT持续存在2MSL的话就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启,可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的)。
- 同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN. 这时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以重发LAST_ACK)。
- CLOSE_WAIT :
一般而言,对于服务器上出现大量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭 socket, 导致四次挥手没有正确完成. 这是一个 BUG. 只需要加上对应的 close 即可解决问题。
3. 三次挥手可以吗?
有可能可以。(要看接受缓冲区里有无任务(数据),如果没有待结束的任务,两次挥手就可以合并(捎带应答))。
4.滑动窗口:
一次发送多条数据,大大提高了性能。
(掌握快重传机制)
5.流量控制:
原因:接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这个时候如果发送端继续发送, 就会造成丢包, 继而引起丢包重传等等一系列连锁反应。
原理:
接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 “窗口大小” 字段, 通过ACK端通知发送端;窗口大小字段越大, 说明网络的吞吐量越高;接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端;发送端接受到这个窗口之后, 就会减慢自己的发送速度;如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 但是需要定期发送一个
窗口探测数据段, 使接收端把窗口大小告诉发送端。
注:
16位数字最大表示65535, 那么TCP窗口最大就是65535字节吗?
实际上, TCP首部40字节选项中还包含了一个窗口扩大因子M, 实际窗口大小是 窗口字段的值左移 M 位。
6.拥塞控制:
(慢启动)
7.延迟应答:
延迟应答策略:
- 每隔N个包延迟应答一次。
- 每隔一段时间(固定时间)延迟应答一次。
(延迟应答时间( 一般为200 )一定要小于超时重传时间(一般为500ms))
8.捎带应答:
在延迟应答的基础上, 会发现, 很多情况下, 客户端服务器在应用层也是 “一发一收” 的.,意味着客户端给服务器说了 “How are you”, 服务器也会给客户端回一个 “Fine, thank you”;那么这个时候ACK就可以搭顺风车, 和服务器回应的 “Fine, thank you” 一起回给客户端。
补充:
粘包,半包问题解决方案:
- 使用“\n”作为流的结束符。(通常选用此方式)
- 每次发送固定大小的流信息。(缺点:造成不必要的网络开销(因为固定大小 则加入空格))
TCP异常情况:
可挽救(有缓冲时间的异常):
- 进程终止: 进程终止会释放文件描述符, 仍然可以发送FIN. 和正常关闭没有什么区别。
- 机器重启: 和进程终止的情况相同。
不可挽救(没有缓冲时间的异常):
- 机器掉电/网线断开: 接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset. 即使没有写入操作, TCP自己也内置了一个保活定时器, 会定期询问对方是否还在. 如果对方不在,也会把连接释放。
另外, 应用层的某些协议, 也有一些这样的检测机制. 例如HTTP长连接中, 也会定期检测对方的状态. 例如QQ, 在QQ断线之后, 也会定期尝试重新连接。