UDP 和 TCP 协议

UDP协议端格式

在这里插入图片描述

16位udp长度: 表示整个数据报(udp首部+udp数据)的长度。
16位校验和:如果校验和出错,直接丢弃。

UDP传输的特点

无连接,不可靠,面向数据报。

1. 无连接:只需要知道对方的ip和端口即可进行传输,不需要连接。

2. 不可靠:没有确认机制,也没有重传机制,如果传输出现错误也不会给应用层返回任何错误信息。

3. 面向数据报:无法灵活控制读写次数,发了几个报文就要收几次,不能多也不能少,就像寄信一样,一封一封的寄出,一封一封的收,不能将不同的信进行合并。

如果发送端调用一次sendto, 发送100个字节, 那么接收端也必须调用对应的一次recvfrom, 接收100个字节,而不能循环调用10次recvfrom, 每次接收10个字

UDP的缓冲区

1. UDP没有真正意义上的发送缓冲区,调用sendto会直接将数据传给内核,由内核将数据交付给网络层进行后续的传递工作。
2. UDP有接收缓冲区,但是接收缓冲区不能保证收到的UDP报的顺序和发送的顺序一致,并且如果缓冲区满了,再到达的UDP报会被丢弃。

UDP的socket既可以读也可以写,UDP是全双工的。

UDP的长度

在udp报头中,有一个16位的udp长度,也就是说一个UDP能传输的数据的最大长度是64K(包含UDP首部),如果大于这个长度,就需要将发送的信息进行分批次的发送。

基于UDP的应用层协议

  • NFS: 网络文件系统
  • TFTP: 简单文件传输协议
  • DHCP: 动态主机配置协议
  • BOOTP: 启动协议(用于无盘设备启动)
  • DNS: 域名解析协议

TCP协议

在这里插入图片描述

  • 32位序号 Seq,通过该序号来确保发送消息的有序性,比如说当前序号为1000,其中发送的数据总共1000字节,那么下次发送时该序号就是2000。

  • 32位确认序号Ack,通过该序号确保对Seq序号的确认,比如说收到的Seq为1000,那么Ack则在Seq的基础上加一,变为1001。

在这里插入图片描述

  • 4位首部长度:以4字节为单位,表示TCP头部的大小,TCP头部的最大大小为15*4=60字节

  • 6位标志位:

    SYN: 只要是报文建立连接的请求,SYN需要被置为1。
    FIN: 该报文为断开连接的请求报文。
    ACK:确认标记为,表示该报文是对历史报文的确认。
    PSH:提示接收端应用程序立即将TCP数据读走。
    URG: 紧急指针标志位,表示紧急指针有效,在16为紧急指针中,记录了紧急数据的偏移量,通过紧急指针可以优先读取数据中的紧急数据。紧急指针指向的数据段从第一个字节到指针的位置,不进入缓冲区直接交付给上层程序,其他数据段进入缓冲区。
    RST: 对方要求重新进行连接,为复位报文段。

超时重传机制

在这里插入图片描述

主机A发送数据给B之后, 可能因为网络拥堵等原因, 数据无法到达主机B,如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发。

在这里插入图片描述但是, 主机A未收到B发来的确认应答, 也可能是因为ACK丢失了

因此主机B会收到很多重复数据. 那么TCP协议需要能够识别出那些包是重复的包, 并且把重复的丢弃掉,这时候我们可以利用前面提到的序列号, 就可以很容易做到去重的效果。

超时时间如何确定?

  • 最理想的情况下, 找到一个最小的时间, 保证 “确认应答一定能在这个时间内返回”
  • 但是这个时间的长短, 随着网络环境的不同, 是有差异的
  • 如果超时时间设的太长, 会影响整体的重传效率
  • 如果超时时间设的太短, 有可能会频繁发送重复的包

1. Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时时间都是500ms的整数倍.
2. 如果重发一次之后, 仍然得不到应答, 等待 2500ms 后再进行重传.
3. 如果仍然得不到应答, 等待 4
500ms 进行重传. 依次类推, 以指数形式递增.
4. 累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接.

TCP的三次握手与四次挥手

在这里插入图片描述

TCP三次握手:

对于服务端:

  • CLOSED -> LISTEN 服务器调用listen进入监听状态,等待客户端的连接。

  • LISTEN->SYN_REVD 一旦监听到连接请求(同步报文段,将该连接放入内核的等待队列中,并向客户端发送SYN+ACK确认报文

  • SYN_REVD->ESTABLISHED:服务端接收到客户端的确认连接,进入ESTABLISHED状态,表示可以进行通信了。

对于客户端:

  • CLOSED -> SYN_SENT:客户端调用connect,发送同步报文段。

  • SYN_SENT->ESTABLISHED:connect调用成功,进入ESTABLISHED,并且想服务端发送ACK表示确认。

此时我们可以理解一下listen的第二个参数:

Linux内核协议栈中为tcp连接管理使用两个队列:

  1. 半连接队列(用来保存处于SYN_SENT 和 SYN_RECV状态的请求)
  2. 全连接队列(accept队列)(用于保存处于established状态,但是应用层没有调用accept取走的请求)

而全连接队列的长度就是listen第二个参数+1, 如果我们进行客户端服务器间的网络通信,服务器仅仅是listen而不去accept,那么刚开始的客户端连接都可以成功进入established状态,当客户端数量大于全连接队列长度时,客户端就只能进入半连接队列,状态也变成了SYN_SENT/SYN_RECV了


TCP4次挥手:

在这里插入图片描述

客户端调用close(fd),向服务端发送FIN标志位,表示结束通信,并且主动变成FIN_WAIT_1状态。

当服务端收到FIN后,主动变成CLOSE_WAIT状态,并且向客户端发送ACK确认报文。

当客户端收到ACK确认报文后,由FIN_WAIT_1状态变为FIN_WAIT_2状态,等待服务器的结束报文段。

服务器进入CLOSE_WAIT状态表示要进行连接关闭,在处理完之前的数据后,服务器调用close,状态由CLOSE_WAIT主动变为LAST_ACK,并且向客户端发送FIN结束报文,等待最后的ACK到来。

当客户端收到服务端的FIN后,状态变为TIME_WAIT,并且向服务端发送ACK确认报文,客户端要等待一个2MSL(Max Segment Life,报文的最大生存时间),才会进入CLOSED状态。

服务器收到了ACK确认报文,彻底进入CLOSED状态。

CLOSE_WAIT状态

如果在我们写的服务器程序中,最后没有进行close,那么服务器端会停留在CLOSE_WAIT状态,而客户端会停留在FIN_WAIT_2状态,相当于4次挥手只完成了前两次,这样的程序是有bug的,并没有真正的进行4次挥手,导致资源不断被消耗。

滑动窗口

在这里插入图片描述

一般在服务器和客户端通信时,一次发送多条数据来提升发送的效率

窗口的大小在指的是无需等待确认应答而可以继续发送数据的最大值,上图的窗口大小就是4000个字节。在发送前面4个数据段时,不需要ACK直接发送,当收到第一个ACK时,窗口向后移动(start++,end++),发送第五个数据段。

操作系统为了维护这个滑动窗口,在内核中有一个发送缓冲区(char sendBuffer[]),而窗口实际上就是由两个整形变量 int start_index 和 int end_index 来维护的,只有确认应答过的数据,才会从缓冲区删掉(实际上就是指针的移动)。

在这里插入图片描述
当1001~2000的数据收到ACK后,start_index += 1000 end_index +=1000,这样就完成了窗口的移动,并且可以发送5001到6000的数据了,那么窗口的大小由什么决定呢? 目前来看,窗口的大小应该是由对方发送报文的16位窗口大小(也就是对方接收缓冲区的剩余空间决定的)但是并不准确,这个我们等一下还会继续谈。
下面我们聊一下,如果在传输过程中发生了丢包怎么办呢?

情况一 ACK丢失

在这里插入图片描述
这种情况没有影响,可以通过后续的ACK来保证前面的数据已经收到了应当,比如说:如果客户端收到了6001,表示1-6000的数据已经全部收到了,不需要重发了。

情况二 数据包丢失

在这里插入图片描述
当某一段报文段丢失之后, 发送端会一直收到 1001 这样的ACK, 就像是在提醒发送端 "我想要的是 1001"一样。
如果发送端主机连续三次收到了同样一个 “1001” 这样的应答, 就会将对应的数据 1001 - 2000 重新发送。
这个时候接收端收到了 1001 之后, 再次返回的ACK就是7001了(因为2001 - 7000)接收端其实之前就已经收到了, 被放到了接收端操作系统内核的接收缓冲区中。
这种机制被称为“高速重发”机制(也叫快重传)

流量控制

接收端处理数据的速度是有限的,如果发送方发送的数据过快,发送方来不及处理和接收,就会导致丢包等一系列的问题,因此TCP支持根据接收端的处理能力来决定发送端的发送速度,这个机制被称为流量控制

  • 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 “窗口大小” 字段, 通过ACK端通知发送端;

  • 窗口大小字段越大, 说明网络的吞吐量越高

  • 接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端,同时发送端也会据此来调整自己的窗口大小。

  • 发送端接受到这个窗口之后, 就会减慢自己的发送速度(调整窗口大小)

  • 如果接收端缓冲区满了, 就会将窗口置为0; 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端

在这里插入图片描述

拥塞控制

虽然TCP有了滑动窗口这个大杀器, 能够高效可靠的发送大量的数据. 但是如果在刚开始阶段就发送大量的数据, 仍然可能引发问题,因为网络上有很多的计算机, 可能当前的网络状态就已经比较拥堵. 在不清楚当前网络状态下, 贸然发送大量的数据,是很有可能引起雪上加霜的。

TCP引入 慢启动 机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据

在这里插入图片描述
这时候有一个概念叫作 拥塞窗口(cwnd congestion window),初始值设为1,每次收到一个ACK后都会增加,然后慢慢变大,刚开始比较小,然后逐渐增大,所以是慢启动机制,当大到一定程度又发生拥塞,会马上回到1,重新开始上述过程。

发送数据的滑动窗口大小 = min(拥塞窗口,对方的窗口大小)

在这里插入图片描述
刚开始是慢开始,但是是以指数形式增长,增速其实特别快,当达到阈值ssthresh后,改为线性增长,此时如果又出现了网络拥塞,则马上减少到1,重新开始慢启动。

延迟应答机制

假设接收端的窗口大小为1M,收到了一个512K的数据,如果立即应答,返回的窗口大小就是512K,但是可能接收端处理数据速度很快,10ms内就处理完了,此时如果稍微等一下再发送ACK,窗口大小就是1M了,这样就大大提升了网络吞吐量,传输效率也大大提升。

那么所有的包都可以延迟应答么? 并不是:
数量限制: 每隔N个包就应答一次
时间限制: 超过最大延迟时间就应答一次

具体的数量和超时时间, 依操作系统不同也有差异; 一般N取2, 超时时间取200ms

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值