认识传输层(UDP与TCP)

传输层主要负责数据能够从发送端发送到接收端。要正确传输就要明确发送端和接收端,这时候IP地址和端口号一起就可以确定一端了。那么他们是怎么唯一标识的呢?

1 端口号(port)

    端口号唯一标识一个主机上进行通信的不同应用程序。

    在TCP/IP协议中,采用源IP地址、目的IP地址、协议号、源端口号、目的端口号这样一个五元组来进行唯一标识一个通信。

1.1 端口号划分

  • 0~1023:知名端口号,HTTP、FTP、SSH这些广为使用的应用层协议,它们的端口号一般都是固定的
  • 1024~65535:操作系统动态分配的端口号,客户端程序的端口号,都是操作系统从这个范围分配的

1.2 知名端口号

ssh服务器:22端口

ftp服务器:21端口

telnet服务器:23端口

http服务器:80端口

https服务器:443端口

我们还可以使用cat /etc/services 来查看知名端口号:

我们自己在写程序要用到端口号时,要尽量避开这些端口,因为一个端口号不可以绑定多个进程;但反过来,一个进程是可以绑定多个端口号的。上一句说一个端口号不可以绑定多个进程,其实并不准确,应该说一般情况下不可以,但通过fork创建的子进程是和父进程绑定的同一个端口号。

2 UDP协议

2.1 UDP 协议格式

  • 源端口号、目的端口号、UDP长度、校验和都是16位表示
  • UDP长度代表了应用层数据报的最大长度
  • 如果校验和出错,该数据报就会直接丢弃

2.2 UDP的特点

  • 无连接:直到对端的IP地址和端口号就可以进行通信,不需要建立连接
  • 不可靠:没有确认机制,没有重传机制;如果因为网络故障没有发送成功,UDP协议层也不会给应用层发送任何错误信息
  • 面向数据报:应用层交给UDP多长的数据,就发送多长的数据,不会拆分,也不会合并

2.3 UDP缓冲区

  • 严格来说,UDP没有发送缓冲区,调用sendto会将数据直接交给内核,由内核将数据传给网络层协议进行后续的操作
  • UDP具有接受缓冲区,但这个缓冲区不能保证接收的的数据顺序,并且一旦缓冲区满了,后来到达的数据就丢失了

注意:UDP协议的协议头有一个最大长度字段,由16位表示,也就是说,UDP协议发送的最大数据长度是64k。

2.4 基于UDP的应用层协议

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

3 TCP协议

TCP协议全称为传输控制协议,顾明思义,就是要对数据传输的过程做一个详细的控制。

3.1 TCP协议格式

  • 源端口号,目的端口号:表示数据从哪来,到哪去
  • 32位序号和确认序号
  • 4位首部长度:表示TCP报头的长度(有多少个4个字节);TCP头部的最大长度是60
  • 保留的6位是标志位:

URG:紧急指针是否有效

ACK:确认号是否有效

PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走

RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段

SYN:请求建立连接;我们把携带SYN标识的称为同步报文段

FIN:通知对方,本端要关闭了,我们把携带FIN标识的称为结束报文段

  • 窗口大小:表示了发送方接下来要发送的数据的多少(根据接收方缓冲区的大小决定)
  • 校验和:发送端填充,CRC校验(循环冗余校验);接收端校验若不通过,说明数据有问题
  • 紧急指针

3.2 TCP的连接管理机制

正常情况下,TCP要经过3次握手,4次挥手建立连接。

TCP的三次握手四次挥手

可以点击上面的链接,进行查看。

3.2.1 确认应答(ACK)机制

TCP将每个字节的数据都做了编号,即为序列号。

TCP协议中,接收方陈宫接收到数据后,会回复一个ACK数据包,表示已经确认收到ACK确认号前的所有数据。也就是告诉发送方,我已经收到了哪些数据,下一次从哪里发送。

比如,发送方收到确认序号为1001,表示序号1~1000的数据已经成功接收到,下面要发送的是序号从1001开始的数据。

3.2.2 超时重传机制

        当发送方发送给接收方的数据,接收方没有收到时,就无法发送ACK包,在一定时间后接收方仍旧没有收到ACK,就会重新发送该数据段;

        还有就是,可能是ack丢包,这时候发送方会发送接收方已经接收到的数据,接收方可以通过序号轻松的将重复的数据去掉。

 那么超时时间是如何确定的呢?

最理想的情况下,找一个最小的时间,保证“确认应答可以在这个时间内返回”,但是这个时间的长短随着网络环境的不同是有差异的,如果重传时间太长,会影响整体的传送效率;如果时间太短,会导致频繁的发送同样的数据。

TCP为了保证在任何情况下都可以高性能的发送数据,会动态计算最大超时时间。


  • Linux中,超时以500ms为单位进行控制,每次判定超时重传的时间为500ms的整数倍
  • 如果重发一次后还得不到应答,下一次等待2*500ms后再次重传
  • 如果仍然得不到应答, 等待 4*500ms 进⾏重传. 依次类推, 以指数形式递增
  • 重传次数累计到一定的数字,TCP会认为网络或对端主机出现异常,强制关闭连接

3.2.3 滑动窗口

上面我们讲利用确认应答以及重传机制可以可靠地进行数据传输,但是这样我们每次要发送一段数据都要等待上一个数据发送成功并返回了ACK之后才能发送,这样的效率未免太慢。这时候就可以选择一次发送多组数据:

在上面的图中,一次可以发送4000个字节的数据,当发送端收到第一个ACK时,滑动窗口向后滑动,4001~5000的数据才可以发送,如下图所示:

发送前四个段时,不需要等待可以同时发送,当收到第一个ACK时,滑动窗口向后滑动,第五段的数据才可以进行发送。操作系统为了维护这个滑动窗口,开辟了发送缓冲区,来记录有哪些数据没有收到应答;只有收到应答的数据才可以从缓冲区删除。滑动窗口越大,网络吞吐率越高。


丢包情况一:ACK丢了

当数据包成功让接收方接收到,接收方发送的ACK丢失的情况下,后续数据的ACK包可以确认。


丢包情况二:数据包丢了

当某一段报文丢失后,比如1001~2000数据丢失,接收方会不停发送1001ack来提醒发送方“我需要的9数据是1001”。当发送方连续收到3次该应答,发送方就会重新发送该数据报。

这种机制也叫“快重传”机制


3.2.4 流量控制

接收端处理数据的能力有限,如果发送端发送数据太快,导致接收端缓冲区满了,这时候如果发送方继续发送数据,就有可能造成丢包。

因此TCP支持根据接收端的处理能力,来决定发送端的发送速度,这个机制叫做流量控制

如上图所示,接收端将自己可以接收的缓冲区大小放入TCP首部中的“窗口大小”字段,通过ACK通知发送端;接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值发送给发送方;发送端接收到这个字段,就会减慢自己的发送速度;如果接受缓冲区满了,就会将窗口值置为0;这时候发送方就不再发送数据,但是需要定时发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。

我们知道,TCP首部中的窗口大小字段由16位表示,最大表示65535,那么TCP窗口最大就是65535字节么?

实际上,TCP首部中还包含了一个窗口扩大因子M,实际窗口大小是窗口字段值左移M位。

3.2.5 慢启动和拥塞控制

虽然有了滑动窗口可以帮助数据进行可靠传输,可是不止接收端的处理能力还有网络状况可能对数据的可靠传输造成影响,如果网络状况不好,一下子发送大量的数据,可能会造成丢包。

TCP引入慢启动,先发送少量的数据,摸清网络的拥堵状况,在决定按照多大的速度传输数据。

慢开始算法:

慢开始门限的初始值设置为 16 个报文段,即 ssthresh = 16

发送端的发送窗口不能超过拥塞窗口 cwnd 和接收端窗口 rwnd 中的最小值。我们假定接收端窗口足够大,因此现在发送窗口的数值等于拥塞窗口的数值。

在执行慢开始算法时,拥塞窗口 cwnd 的初始值为 1,发送第一个报文段 M0。

发送端收到 ACK1 (确认 M0,期望收到 M1)后,将 cwnd 从 1 增大到 2,于是发送端可以接着发送 M1 和 M2 两个报文段。

接收端发回 ACK2 和 ACK3。发送端每收到一个对新报文段的确认 ACK,就把发送端的拥塞窗口加 1。现在发送端的 cwnd 从 2 增大到 4,并可发送 M3 ~ M6共 4个报文段

发送端每收到一个对新报文段的确认 ACK,就把发送端的拥塞窗口加 1,因此拥塞窗口 cwnd 随着传输次数按指数规律增长。

拥塞控制算法:

拥塞窗口 cwnd 增长到慢开始门限值 ssthresh 时(即当 cwnd = 16 时),就改为执行拥塞避免算法,拥塞窗口按线性规律增长。

假定拥塞窗口的数值增长到 24 时,网络出现超时(表明网络拥塞了)。

更新后的 ssthresh 值变为 12(即发送窗口数值 24 的一半),拥塞窗口再重新设置为 1,并执行慢开始算法。

当 cwnd = 12 时改为执行拥塞避免算法,拥塞窗口按按线性规律增长,每经过一个往返时延就增加一个 MSS 的大小。

少量丢包,仅仅触发超时重传;大量丢包,认为网络拥塞。

3.2.6 延迟应答

如果当接收方收到数据就立刻应答,比如接收缓冲区是1M大小,接受了500k的数据,那么返回的窗口大小就是500k;但是接收方的处理数据的速度可能很快,如果延迟200ms的时间在进行应答,返回的窗口大小就是1M,这样网络吞吐率就会大大提高。这就是延迟应答。延迟应答的目的就是为了提高传输效率。

那么,所有的包都可以延迟应答么?不是,

数量限制:每隔N个包就要应答一次;

时间限制:超过最大延迟时间就应答一次;

不同的操作系统的N和最大延迟时间不同,一般情况下,N取2,最大延迟时间为200ms。

3.2.7 捎带应答

在延迟应答的基础上, 我们发现, 很多情况下, 客户端服务器在应⽤层也是 "⼀发⼀收" 的. 意味着客户端给服务器说了 "How are you", 服务器也会给客户端回⼀个 "Fine, thank you";那么这个时候ACK就可以搭顺⻛⻋, 和服务器回应的 "Fine, thank you" ⼀起回给客户端。
 

3.2.8 面向字节流

创建⼀个TCP的socket, 同时在内核中创建⼀个 发送缓冲区 和⼀个 接收缓冲区;

  • 调⽤write时, 数据会先写⼊发送缓冲区中;
  • 如果发送的字节数太⻓, 会被拆分成多个TCP的数据包发出;
  • 如果发送的字节数太短, 就会先在缓冲区⾥等待, 等到缓冲区⻓度差不多了, 或者其他合适的时机发送出去;
  • 接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区;然后应⽤程序可以调⽤read从接收缓冲区拿数据;
  • 另⼀⽅⾯, TCP的⼀个连接, 既有发送缓冲区, 也有接收缓冲区, 那么对于这⼀个连接, 既可以读数据,也可以写数据. 这个概念叫做 全双⼯

由于缓冲区的存在, TCP程序的读和写不需要⼀⼀匹配, 例如:

  • 写100个字节数据时, 可以调⽤⼀次write写100个字节, 也可以调⽤100次write, 每次写⼀个字节;
  • 读100个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以⼀次read 100个字节, 也可以⼀次read⼀个字节, 重复100次;

3.2.9 粘包问题

首先,明确粘包问题中的“包”,指的是应用层的数据包。在TCP协议中,没有如同UDP一样的“报文长度”的字段,但是有一个序号的字段,站在传输层的角度,数据是一个个报文过来,按照序号排好的数据放在缓冲区中;但是在应用层的角度,只看到了一连串的字节数据,并不知道一个报文的开始和结束。这时候,如果上一个没读完,下一个发送了新的数据,要读的时候,就可能读到前面没读完的数据,对该进程来说是错误的数据。这就是粘包问题

那么,如何避免呢?


  • 对于定⻓的包, 保证每次都按固定⼤⼩读取即可;
  • 对于变⻓的包, 可以在包头的位置, 约定⼀个包总⻓度的字段, 从⽽就知道了包的结束位置;
  • 对于变⻓的包, 还可以在包和包之间使⽤明确的分隔符(应⽤层协议, 是程序猿⾃⼰来定的, 只要保证分隔符不和正⽂冲突即可);

4 TCP/UDP对比

  • TCP用于可靠传输的情况,应用于文件传输,重要状态更新等场景
  • UDP用于高速传输和实时性要求较高的通信领域;另外,UDP可以用于广播。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值