计算机网络(5):运输层
OSI 模型与 TCP/IP 协议
OSI七层协议模型 (open system interconnection)
- 应用层:为应用数据提供服务
- 表示层:数据格式转化,数据加密
- 会话层:建立、维护和管理会话
- 传输层:建立、维护和管理端到端的链接,控制数据传输的方式
- 网络层:数据传输线路选择,IP地址及路由选择
- 数据链路层:物理通路的发送和数据包的划分,附加Mac地址到数据包
- 物理层:01比特流的转换
数据传输由顶向下,下层为上层提供服务
TCP/IP四层协议模型
- 应用层:负责处理特定的应用程序细节,如ftp,http ,smtp,ssh 等;
- 运输层:主要为两台主机上的应用提供端到端的通信,如TCP,UDP。
- 网络层(互联网层):处理分组在网络中的活动,比如分组的选路。
- 链路层(数据链路层/网络接口层):包括操作系统中的设备驱动程序、计算机中对应的网络接口卡,01比特流的转换
协议封装
下层协议通过封装为上层协议提供服务。
应用程序数据在发送到物理网络上之前,将沿着协议栈从上往下依次传递。每层协议都将在上层数据的基础上加上自己的头部信息(有时也包括尾部信息),以实现该层的功能。
TCP 协议头部
- 源端口号和目的端口号:再加上IP首部的源IP地址和目的IP地址可以唯一确定一个TCP连接
- 数据序号:表示在这个报文段中的第一个数据字节序号
- 确认序号:仅当
ACK
标志为1
时有效。确认号表示期望收到的下一个字节的序号 - 偏移:就是头部长度,有4位,跟 IP 头部一样,以4字节为单位。最大是60个字节
- 保留位:6位,必须为0
- 6个标志位:
URG
-紧急指针有效;
ACK
-确认序号有效;
PSH
-接收方应尽快将这个报文交给应用层;
RST
-连接重置;
SYN
-同步序号用来发起一个连接;
FIN
-终止一个连接; - 窗口字段:16位,代表的是窗口的字节容量,也就是TCP的标准窗口最大为2^16 - 1 = 65535个字节;
- 校验和:源机器基于数据内容计算一个数值,收信息机要与源机器数值结果完全一样,从而证明数据的有效性。检验和覆盖了整个的TCP报文段:这是一个强制性的字段,一定是由发送端计算和存储,并由接收端进行验证的;
- 紧急指针:是一个正偏移量,与序号字段中的值相加表示紧急数据最后一个字节的序号。TCP的紧急方式是发送端向另一端发送紧急数据的一种方式;
- 选项与填充(必须为4字节整数倍,不够补0):最常见的可选字段的最长报文大小 MSS(Maximum Segment Size),每个连接方通常都在一个报文段中指明这个选项。它指明本端所能接收的最大长度的报文段。
该选项如果不设置,默认为 536(20+20+536=576字节的IP数据报)
三次握手
1.客户端向服务器发送 SYN:
客户端发起连接请求,发送一个TCP报文段,其中包含SYN(同步)标志,并选择一个初始序列号Seq=X(X一般为1)。随后客户端进入SYN-SENT 阶段。
这个报文段不包含数据,但在TCP首部中有SYN标志位。
2.服务器收到SYN并发送ACK + SYN:
服务器收到客户端的SYN后,结束 LISTEN 阶段,回应一个确认(ACK)标志,确认收到了客户端的请求,同时也发送一个SYN标志。
服务器选择一个自己的初始序列号(Seq=y)。随后服务器端进入SYN-RCVD 阶段。
3.客户端收到ACK + SYN:
客户端收到服务器的确认和SYN后,结束 SYN-SENT 阶段,回应一个ACK标志(Ack=y+1),确认收到服务器的响应。随后客户端进入ESTABLISHED阶段。
从此时开始,数据传输的双向连接建立完成,可以开始进行数据传输。
在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性。一旦出现某一方发出的TCP报文丢失,便无法继续"握手",以此确保了"三次握手"的顺利完成。
滑动窗口
维持发送方/接收方缓冲区,缓冲区是用来解决网络之间数据不可靠的问题,例如丢包,重复包,出错,乱序。在TCP协议中,发送方和接受方通过各自维护自己的缓冲区。通过商定包的重传机制等一系列操作,来解决不可靠的问题。
正常情况:
4号包对方已经被接收到,所以被涂成了灰色。“窗口”就往右移一格,这里只要保证“窗口”是7格的。 我们就把11号包读进了我们的缓存。进入了“待发送”的状态。8、9号包已经变成了黄色,表示已经发送出去了。接下来的操作就是一样的了,确认包后,窗口往后移继续将未发送的包读进缓存,把“待发送“状态的包变为”已发送“。
丢包情况:
有可能我们包发过去,对方的Ack丢了。也有可能我们的包并没有发送过去。从发送方角度看就是我们没有收到Ack。
一般情况:一直在等Ack。如果一直等不到的话,我们也会把读进缓存的待发送的包也一起发过去。但是,这个时候我们的窗口已经发满了。所以并不能把12号包读进来,而是始终在等待5号包的Ack。
如果我们这个Ack始终不来怎么办呢? 采用超时重传机制解决:
发送端每发送一个报文段,就启动一个定时器并等待确认信息;接收端成功接收新数据后返回确认信息。若在定时器超时前数据未能被确认,TCP就认为报文段中的数据已丢失或损坏,需要对报文段中的数据重新组织和重传。(重传超时时间: RTO)
四次挥手
- 客户端发送断开 TCP 连接请求的报文,其中报文中包含 seq 序列号,是由发送端随机生成的,并且还将报文中的 FIN 字段置为1 ,表示需要断开TCP连接。(FIN=1,seq=u,u由客户端随机生成)
- 服务端会回复客户端发送的 TCP 断开请求报文,其包含 seq 序列号,是由回复端随机生成的,而且会产生 ACK 字段,ACK 字段数值是在客户端发过来的 seq 序列号基础上加 1进行回复,以便客户端收到信息时,知晓自己的 TCP 断开请求已经得到验证。(ACK=1,FIN=1,ack=u+1,seq=v,v由服务端随机生成)
- 服务端在回复完客户端的 TCP 断开请求后,不会马上进行TCP连接的断开,服务端会先确保断开前,所有传输到A的数据是否已经传输完毕,一旦 确认传输数据完毕,就会将回复报文的 FIN 字段置1, 并且产生随机 seq 序列号。(ACK=1,FIN=1,ack=u+1,seq=w,w由服务端随机生成)
- 客户端收到服务端的TCP断开请求后,会回复服务端的断开请求,包含随机生成的 seq 字段和ACK字段,ACK字段会在服务端的TCP断开请求的seq基础上加1,从而完成服务端请求的验证回复。(ACK=1,FIN=1,ack=w+1,seq=u+1)
分包和粘包
TCP分包
场景:发送方发送字符串”helloworld”,接收方却分别接收到了两个数据包:字符串”hello”和”world”。
发送端发送了数量较多的数据,接收端读取数据时候数据分批到达,造成一次发送多次读取;
造成分包的原因:
TCP是以段(Segment)为单位发送数据的,建立TCP链接后,有一个最大消息长度(MSS).如果应用层数据包超过MSS,就会把应用层数据包拆分,分成两个段来发送。这个时候接收端的应用层就要拼接这两个TCP包,才能正确处理数据。
相关的,路由器有一个MTU( 最大传输单元)一般是1500字节,除去IP头部20字节,留给TCP的就只有MTU-20字节。所以一般TCP的MSS为MTU-20=1460字节。
当应用层数据超过1460字节时,TCP会分多个数据包来发送。
TCP 粘包
场景:发送方发送字符串”hello“和“world”,接收方却接收到了一个字符串”helloworld”。
发送端发送了几次数据,接收端一次性读取了所有数据,造成多次发送一次读取;通常是网络流量优化,把多个小的数据段集满达到一定的数据量,从而减少网络链路中的传输次数
造成TCP粘包的原因:
TCP为了提高网络的利用率,会使用一个叫做Nagle的算法,该算法是指,发送端即使有要发送的数据,如果很少的话,会延迟发送。如果应用层给TCP传送数据很快的话,就会把两个应用层数据包“粘”在一起,TCP最后只发一个TCP数据包给接收端。
分包和粘包解决方案
发送数据前,给数据附加两字节的长度:
4字节 | N个字节 | |
---|---|---|
FBEB | 数据长度N | 数据内容 |
- 包标识: 包头部的特殊标识,用来标识包的开始
- 数据长度:数据包的大小,固定长度,2、4 或者8字节。
- 数据内容:数据内容,长度为数据头定义的长度大小。
a)发送端:先发送包表示和长度,再发送数据内容。
b)接收端:先解析本次数据包的大小N,再读取N个字节,这N个字节就是一个完整的数据内容。
UDP 通信
在使用UDP通讯时,不需要区分客户端和服务端,但在通讯过程中,我们可以将发送端定义为服务端,接收端定义为客户端。因此,在配置UDP通讯时,我们只需要开启本机端口即可。在发送数据时,需要设置接收的IP地址和端口。UDP模式下,任意一端都可以发送数据,只需要在发送端添加目标IP和端口即可。
UDP(用户数据报协议)是无连接的协议,每个UDP数据包都是独立的,没有连接状态的维护。服务器在接收UDP数据包时,不需要为每个客户端连接创建额外的进程或线程,因为它不必保持连接状态。每个数据包都包含足够的信息,服务器可以直接处理它们,而不必关心连接的保持。
UDP通信,是不保证有序到达的数据报服务。(在局域网内,使用UDP已很可靠)
使用UDP通信与TCP通信使用上的区别:
1)创建套接字时的 type(参数2)不同。
TCP通信,使用SOCK_STREAM
UDP通信,使用SOCK_DGRAM
2)发送数据和接收数据时,使用的接口不同
TCP通信
发送数据,使用write
(或send
)
接收数据,使用read
(或recv
)
UDP通信
发送数据,使用sendto
接收数据,服务器端使用recvfrom
客户端使用recv
3)不需要使用listen
4)不需要先建立连接(TCP客户端和服务器端分别使用connect
和receive
建立连接)
UDP适用于那些对实时性要求高、能够容忍一定数据丢失的场景,因为它的无连接性和较低的开销使其更适合快速而简单的通信。在这样的场景下,服务器可以轻松地处理多个客户端的独立请求,而无需为每个连接创建额外的进程或线程。
。。。