dustland

dustball in dustland

计算机网络-传输层

计算机网络-传输层

传输层

概念

网络层提供点到点服务,也就是主机到主机的服务

传输层提供端到端服务,也就是进程到进程的服务

image-20221128103802707

端口号

寻址方式 寻址范围
链路层 MAC地址
网络层 ip地址 ipv4地址32位
传输层 端口号 端口号16位

服务端端口号规定

image-20221128104113012

客户端都是临时端口,不用管到底用的哪个端口

套接字

socket=IP地址+端口号

一个套接字唯一标识了一个进程

一个TCP连接两头是两个套接字,即一个TCP连接被一对套接字决定

差错控制

可靠传输:不错,不丢,不乱

每一层的校验都只校验本层数据

差错控制的目的 是否可靠
数据链路层 通过CRC校验,保证一个帧中没有比特差错,但不能保证丢不丢帧 否,只能保证不错
网络层IP协议 只针对网络层包的头部进行CRC校验
传输层TCP协议 保证做到无传输差错

应用场景

传输层协议 应用场景
TCP 客户端和服务端多次交互入访问页面HTTP
传输文件FTP
电子邮件POP3,SMTP
UDP 实时聊天通信,DNS,多播,广播
对等网络技术P2P
部分路由协议RIP
网络时间管理NTP
简单网络管理SNTP

UDP

UDP首部

UDP首部

源端口,目的端口各16位,总长度16位,校验和16位

校验和

UDP的校验和=UDP伪首部+UDP首部+数据

这个伪首部指,源地址、目的地址、协议类型(0x11),一个字节的全0,一个字节的UDP数据长度,对齐填充2字节的0,整个伪首部共12个字节。

UDP伪首部是为了计算校验和而临时存在的,在计算之前由主机加上,计算之后立刻扔掉,不会参与传输

UDP伪首部和IP首部无关,不能理解为借用的IP首部

UDP伪首部不是IP首部

伪首部中的UDP长度就是UDP首部+UDP数据的长度,不需要考虑对齐填充,是多少就是多少

伪首部+UDP首部+数据一起计算校验和。

image-20221128110006207

具体计算方法:

16位一组相加,最高位进位回卷,和Sum取反得到CheckSum

在计算校验和时,此时UDP首部的校验和字段先置零,伪首部中的UDP长度就是实际的UDP长度,15,不用考虑对齐填充

image-20221128110218290

UDPの特点

1.报文在发送方这里不会拆分,发送方一次交付一个完整报文.

注意强调了发送方,因为网络层才不会管你拆不拆,大于MTU的报必须拆

比如如果发送方不管三七二十一发了一个巨大的上万字节的UDP报文

如果数据链路层使用以太网,那么每个以太网帧最大是1500字节,也就是说网络层的MTU=1500(Maximum Transmission Unit).

显然一个上万字节的UDP数据报无法直接发送,因此路由器会进行报文分割,把该UDP数据报拆分成若干不大于1500字节的包,分开发送

最终在接收方还是需要组装的

2.无流量控制,无差错控制,无拥塞控制

差错控制要求发现错误时要求重传,但是UDP数据报的校验和如果发现错误,直接被路由器或者主机丢弃,不会要求重传

TCP

分段传输:虚电路建立之后,看上去TCP协议直接发送和接收字节流,就像访问硬盘一样,但是下层实际上还是数据报实现的

一个图是需要切成好多段,扔给网络层以报文形式交付

TCP协议的熟知端口号

image-20230203162222069

特别注意

20-FTP.Data

21-FTP.Control

53-DNS

字节流

啥玩意叫字节流?

啥玩意叫流?Stream,在计算机里就是顺序读取或者写入的字节序列.

包括从硬盘读取的文件,从键盘输入的字符序列,从网络获取的字符序列.都叫流

叫做字节流,是因为还有一个字符流

两者的区别是,字节流使用8位一个字节为信息单元,一个字节传输一个信息,比如一个字母

而字符流使用16位两个字节作为信息单元,使用unicode编码传输信息

image-20221128111751747

"流"是针对应用层上的应用程序而言的,在应用程序看来,他调用read函数从硬盘读取字节流和调用recv函数从套接字获取字节流没有区别.

反正就是得用while(!read.eof()){read(buf,n,file);...}这样不停地从缓冲区取出字节,因为一个文件的传输,是像水流一样,源源不断地抵达缓冲区的,不可以一下子全部获取

传输层只管给应用程序的缓冲区写入数据,不管应用程序拿着数据干了啥

应用程序只管从缓冲区读取数据,不管传输层从哪里搞来的数据

因此,传输层单蹦个地顺序传送比特位,还是单蹦个顺序传送字节,抑或是分段每次传送成千个字节即分段,应用程序不关心,全靠传输层自由发挥了

显然传输层应该选择分段发送,这样效率高

为什么效率高?

对于网络来说头部长度固定,数据部分越长有效信息比例越高,需要发送的数据报越少,网络负载也就轻

对于主机来说,每个字节就发送一次,需要频繁地系统调用(显然访问网络这种外设需要系统调用),开销太大

首部格式

image-20221128111530489
Source/Destination port address

源/目的端口地址

sequence number

字节序列号,一个TCP包可以发送若干数据字节,每一个数据字节都编一个字节序列号,

在首部中sequence number表明本TCP报文中第一个数据字节的编号

本字段用于分段系统

acknowledgment number

确认号字段,期望收到的,对方下一个报文段数据的,第一个字节的序号

本字段用于分段系统

HLEN

header length首部长度,其单位是4字节

比如HLEN=5=0101b,表示首部长度为5*4=20字节

Reserved

保留字段,6位,目前全置零

控制字段

6位

用于TCP连接的流量控制,连接建立终止,连接失败和数据传送方式

image-20221128113731360

当URG=1时,紧急指针Urgent pointer才有效

如何体现紧急?

正常情况下一个TCP报文段会拆分重组

整个重组利索之后才会交付给应用程序

而紧急数据无需等待重组,直接交给应用程序

ACK,确认,1代表acknoledgment number字段有效

PSH,请求急迫,发送端不需等待窗口填满才发送

PSH用于什么情况?

缓冲区与PSH

进程A向socket写东西,实际上就是写到发送缓冲区中,此时并没有实际往网络上发送,啥时候发送呢?得等到缓冲区满了才发,这就像是机场包车的包不满不走一样

而PSH位就是要强迫发送,即使车没有坐满人,也把枪夹到司机头上给我立刻发车

有意义吗?急着发车干啥?还真有意义

有的乘客不在乎自己坐车多花的开销,他急着办事.

正如有些服务的及时性比效率更优先

比如ssh服务

客户端这会急着想看服务端根目录下有啥,于是客户端想发送一个ls命令,如果不加psh位,好吧你等着吧,啥时候缓冲区满了才发送.实际上ssh服务是psh=1的

image-20230203172915324

RST,重置连接

SYN,同步信号,建立连接时使用

FIN,结束

window size

窗口大小,单位字节

用于控制对方发送的数据量

根据自身缓冲区剩余空间大小,决定接收窗口大小,通知对方发送窗口上限

checksum

检验和

TCP检验和=TCP伪首部+TCP首部+TCP数据

其中TCP伪首部和UDP伪首部一模一样

urgent pointer

紧急指针字段,

如果有紧急数据,则一定放在TCP数据的最开始

紧急指针表明紧急数据的大小,单位字节

option

选项字段,长度可变

通常不用这个字段

TCP之规定一种选项,MSS,最大报文段长度,即TCP数据的最大长度

序号系统

由于TCP报文需要分段,因此需要引入一套编号机制,让分段和重组有序

TCP首部的sequence number,acknowledgment number两个字段和ACK,SYN两个标志位就是为序号系统服务的

需要牢记的是:

编号是给每个字节的编号,不是给分段的编号!

编号是给每个字节的编号,不是给分段的编号!

编号是给每个字节的编号,不是给分段的编号!

所有数据的第一个字节是一个\([0,2^{32})\)内的随机数

sequence number,该TCP报文段段第一个数据字节的编号

acknowledgment number,希望接收的下一个报文段的第一个数据字节的编号.也表明接收方已经正确接收该编号-1之前的所有字节

比如这么一个问题

image-20230203173943002

显然第一问是100-70=30

第二问,主机B接收到的字节序列应该是\([70,99]\),因此ack number=100,表明希望接收编号为100的字节,并且告知发送方,编号为99及之前的所有字节都已经正确接收了

连接建立&终止

三次握手建立连接

image-20230203224344990

服务端是被动打开的,而客户端是主动打开的

三次握手建立连接的目的是,证明通信双方的收发都正常

第一次握手,客户端啥也不知道,但是服务端知道客户端不哑,自己不聋

第二次握手,客户端知道了服务端收到了自己的消息,证明自己不哑,这次收到消息证明自己不聋.

此时只剩最后一步,此时服务端不知道自己是否哑巴

第三次握手,服务端收到客户端对第二次握手的确认,因此证明服务端不哑

第三次握手时,实际上客户端已经可以在TCP数据中,写上对服务端请求什么了

然而实际上HTTP协议中,三次握手不涉及任何有效数据传输,三次握手建立之后,客户端会另外发送GET请求

image-20230203225616420

四次挥手断开连接的目的是:

首先客户端主动提出FIN分手,表明客户端没有要求了

服务端收到客户端的分手请求后,立刻回复ACK收到,但是如果服务端还有没说完的话,还可以继续说.

这个阶段叫做半关闭阶段,客户端只能回复收到,不会再有新的请求

如果服务端也说完了,没有其他话要说了,就发送FIN

然后客户端收到FINI之后知道服务端也没得说了,回复收到

到此整个连接关闭

流量控制

区分流量控制和拥塞控制:

流量控制是避免接收方来不及接收,缓冲区溢出

拥塞控制是避免网络拥塞

为啥要进行流量控制?

接收方的缓冲区大小有限,要保证接收方来得及接收消息并腾出缓冲区

如果发送方发的过快,接收方处理慢,缓冲区满了,那么后来的消息就会被直接丢弃

如何进行流量控制?滑动窗口算法

滑动窗口算法

image-20230204190749379 \[ 发送方滑动窗口大小swnd=min(接收方滑动窗口大小rwnd,拥塞窗口大小cwnd) \] 其中rwnd是接收方缓冲区剩余空间大小

比如接收方缓冲区本身4KB,发送方发了一个1K的报文,填到接收方缓冲区中,此时接收方rwnd=3000,接收方就得在ACK报文中报告自己的rwnd大小,让发送方心里有数,后面该法多少

cwnd是发送方自己维护的,发送方根据接收方的回应报文丢失情况,推测网络的拥塞程度,动态调整cwnd的大小

TCP滑动窗口和数据链路层滑动窗口的区别:

TCP滑动窗口的单位是字节,而数据链路层滑动窗口的单位是帧

数据链路层的滑动窗口大小是固定的,而TCP滑动窗口大小根据实时情况改变

如图所示的滑动窗口(只是一个例子,实际上一个滑动窗口有成百上千字节)中,cwnd=20,rwnd=9,

这就意味着接收方此时的缓冲区只有9个字节的空间了,发送方顶多再发送9字节

image-20230208173522440

此时发送方啥状态呢?目前滑动窗口中的200,201,202三个字节都已经发送,可能是一个报文同时发走的,也可能是分批发走的,但是这不重要,重要的是,目前尚未得到接收方的ACK回复.发送方现在还可以接着发送203~208这6个字节.

如果发完了这6个字节仍热没有收到回复,就不能发了,得等等

时序图

image-20230208174029314

首先发送方得等接收方告知自己的接收窗口大小

拿到这个数之后,发送方就可以在这个接收窗口大小和拥塞窗口大小中娶一个最小值,然后放心地发送这么多字节

这些字节不必是一个报文发走的,这要看链路层对一个数据帧的限制,比如以太网帧中的数据不能超过1500字节.因此,即使swnd=min(cwnd,rwnd)=2400,也得分成多个报文发送

接收方会对发送方的每一个报文都进行回复,回复内容包括:

1.期待接收的下一个字节编号

2.当前接收方窗口剩余大小

当接收方的回复报文中,swnd=0时,发送方就得停下等接收方消化消化.

接收方消化一阵子之后,缓冲区有比较大的空间,能够容纳一个大帧时,才会主动发送更新报文.告知发送方,可以继续灌输了

等待缓冲区有较大空间的目的是,防止糊涂窗口综合征

这个更新报文的内容包括:

1.期待接收的下一个字节编号

2.当前接收方窗口剩余大小

为了防止这个更新报文丢包,发送方有一个坚持计时器.当发送方接到swnd=0的回复报文就开始了.如果在到时之前收到更新报文自然最好.如果没有收到,则发送方认为更新报文在路上丢了,于是发送方发送一个1字节的探测报文,提醒接收方更新报文丢失了.

差错控制

TCP的差错控制有三个手段

1.校验和,接收方收到坏段,丢弃并要求重传,通过ACK+坏段序号 要求重传

2.确认

发送方发出的每一个数据段都需要ACK确认

不携带数据但是占用序号的控制段也需要确认

ACK段不需要确认,因为ACK本身就是确认用的

3.重传

当段损坏,丢失或者超时,需要重传

ACK段不需要重传

重传的情形:

1.丢失段

image-20230208175609900

2.3ACK快速重传

image-20230208175633242

拥塞控制

拥塞控制可以理解为网络流问题

一条主干道的带宽是1000Mbps,其支线的带宽和是1500Mbps,如果所有支线都满载传输,则骨干路由器就得缓存支线的数据报文,满满地往主干道发.

如果骨干路由器缓冲区溢出,就有数据丢包了

拥塞控制还是滑动窗口算法,并且和流量控制兼容,体现在 \[ swnd=min(cwnd,rwnd) \]

慢启动和拥塞避免
image-20230208180115427

最初慢启动阶段,cwnd=1,发一个报文,收到ACK,这就意味着一次发一个包,网络可以承受,于是蹬鼻子上脸,cwnd=2,发俩报文,等俩ACK,如果又都受到了,那就更加猖狂

一直到慢启动阈sstresh,之后就不能指数扩大cwnd了,需要加性增大,也就是发一个包收到ACK就cwnd扩大1,一直这样直到计时器超时,说明达到网络流量上限了,

此时立刻回到慢启动阶段,cwnd=1,并重新设置ssthresh阈值为超时cwnd的一半

之后重复上述过程

3ACK快速恢复
image-20230208180505456

能够收到3ACK说明网络只是轻度拥塞

此时直接ssthresh降为收到3ACK时cwnd的一半,然后设置cwnd=ssthresh,然后重复加性增大阶段

状态转移图
image-20230208180715552