传输层协议
TCP(Transmission Control Protocol),传输控制协议
UDP(User Datagram Protocol),用户数据报协议
tcp,udp对比
TCP | UDP | |
---|---|---|
连接性 | 面向连接 | 无连接 |
可靠性 | 可靠传输 | 不可靠传输,尽最大努力交付,可能丢包 |
首部占用空间 | 大 | 小 |
传输速率 | 慢 | 快 |
资源消耗 | 大 | 小 |
应用场景 | 浏览器、文件传输、邮件发送 | 音视频通话、直播 |
应用层协议 | HTTP、HTTPS、FTP、SMTP、DNS | DNS |
首部字节 | 20-60字节 | 8字节 |
TCP 数据
数据偏移
占4位,取值范围是0x0101~0x1111 乘以4:首部长度(Header Length) 首部长度是20~60字节
保留
占6位,目前全为0。有些资料中,TCP首部的保留(Reserved)字段占3位,标志(Flags)字段占9位
检验和
由 伪首部+首部 + 数据 计算出来
标志位
URG(Urgent) 当URG=1时,紧急指针字段才有效。表明当前报文段中有紧急数据,应优先尽快传送
ACK(Acknowledgment) 当ACK=1时,确认号字段才有效
PSH(Push)
RST(Reset) 当RST=1时,表明连接中出现严重差错,必须释放连接,然后再重新建立连接
SYN(Synchronization) 当SYN=1、ACK=0时,表明这是一个建立连接的请求 若对方同意建立连接,则回复SYN=1、ACK=1
FIN(Finish) 当FIN=1时,表明数据已经发送完毕,要求释放连接
序号
4字节,首先,在传输过程的每一个字节都会有一个编号 在建立连接后,序号代表:这一次传给对方的TCP数据部分的第一个字节的编号
确认号(Acknowledgment Number)
占4字节 在建立连接后,确认号代表:期望对方下一次传过来的TCP数据部分的第一个字节的编号
窗口(Window)
占2字节 这个字段有流量控制功能,用以告知对方下一次允许发送的数据大小(字节为单位)
UDP 数据
UDP长度
占16位,首部长度+数据部分长度。TCP头部仅记录了TCP首部长度。
传输层的数据长度 = 网络层数据长度 - 网络层首部长度 - 传输层首部长度
检验和
由 伪首部+首部 + 数据 计算出来
TCP 详解
可靠传输
ARQ 自动重传技术
停止等待ARQ协议
确认丢失,确认迟到,
一次发一个,超时重发
重传超系统设定次数,会发生RST报文,断开链接
缺点:一次一个,效率低
连续ARQ协议 + 滑动窗口协议
序号,确认号,窗口
SACK
一次发多个,
缺点:中间数据包丢失,会重发从丢失数据包往后的所有数据,后面数据被重复发送降低性能
SACK
改善上面的问题,可以告诉对方哪些数据丢失
只发送丢失数据,不会发后续的已经接收的数据
SACK信息存储在TCP首部的选项部分
缺点:受选项部分长度限制,SACK最多携带4组边界信息
TCP 流量控制
what
让发送方的发送速率不要太快,让接收方来得及接收处理
why
如果接收方的缓存区满了,发送方还在疯狂着发送数据 接收方只能把收到的数据包丢掉,大量的丢包会极大着浪费网络资源 所以要进行流量控制
原理
通过确认报文中窗口字段来控制发送方的发送速率 发送方的发送窗口大小不能超过接收方给出窗口大小 当发送方收到接收窗口的大小为0时,发送方就会停止发送数据
窗口0特殊情况
一开始,接收方给发送方发送了0窗口的报文段 后面,接收方又有了一些存储空间,给发送方发送的非0窗口的报文段丢失了 发送方的发送窗口一直为零,双方陷入僵局
解决方案 当发送方收到0窗口通知时,这时发送方停止发送报文 并且同时开启一个定时器,隔一段时间就发个测试报文去询问接收方最新的窗口大小 如果接收的窗口大小还是为0,则发送方再次刷新启动定时器
TCP 拥塞控制
what
防止过多的数据注入到网络中 避免网络中的路由器或链路过载
涉及到所有的主机、路由器 以及与降低网络传输性能有关的所有因素 是大家共同努力的结果
与流量控制区别,流量控制是点对点通信的控制
拥塞控制方法
慢开始
拥塞避免
快速重传
快速恢复
相关缩写
MSS(Maximum Segment Size):每个段最大的数据部分大小 在建立连接时确定
cwnd(congestion window):拥塞窗口
rwnd(receive window):接收窗口
swnd(send window):发送窗口 swnd = min(cwnd, rwnd)
慢开始
cwnd的初始值比较小,然后随着数据包被接收方确认(收到一个ACK) cwnd就成倍增长(指数级)
拥塞避免
ssthresh(slow start threshold):慢开始阈值,cwnd达到阈值后,以线性方式增加
拥塞避免(加法增大):拥塞窗口缓慢增大,以防止网络过早出现拥塞
乘法减小:只要网络出现拥塞,把ssthresh减为拥塞峰值的一半,同时执行慢开始算法(cwnd又恢复到初始值)
当网络出现频繁拥塞时,ssthresh值就下降的很快
快速重传
接收方 每收到一个失序的分组后就立即发出重复确认 使发送方及时知道有分组没有到达 而不要等待自己发送数据时才进行确认
发送方 只要连续收到三个重复确认(总共4个相同的确认),就应当立即重传对方尚未收到的报文段 而不必继续等待重传计时器到期后再重传
快恢复
当发送方连续收到三个重复确认,说明网络出现拥塞 就执行“乘法减小”算法,把ssthresh减为拥塞峰值的一半
与慢开始不同之处是现在不执行慢开始算法,即cwnd现在不恢复到初始值 而是把cwnd值设置为新的ssthresh值(减小后的值) 然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大
拥塞控制 - 发送窗口的最大值
发送窗口的最大值:swnd = min(cwnd, rwnd)
当rwnd < cwnd时,是接收方的接收能力限制发送窗口的最大值
当cwnd < rwnd时,则是网络的拥塞限制发送窗口的最大值
链接管理
建立连接
建立连接状态
CLOSED:client处于关闭状态
LISTEN:server处于监听状态,等待client连接
SYN-RCVD:表示server接受到了SYN报文,当收到client的ACK报文后,它会进入到ESTABLISHED状态
SYN-SENT:表示client已发送SYN报文,等待server的第2次握手
ESTABLISHED:表示连接已经建立
建立连接三次握手过程
1、客户端 ---- SYN=1,ACK=0,seq=x 客户端CLOSED->SYN-SENT
2、服务端 ---- SYN=1,ACK=1,seq=y, ack=x+1 服务端LISTEN->SYN-RCVD
3、客户端 ---- ACK=1,seq=x+1, ack=y+1 客户端 SYN-SENT->ESTABLISHED
服务端收到第三次握手数据后 SYN-RCVD -> ESTABLISHED
三次握手过程中,会交换一些信息
比如MSS、是否支持SACK、Window scale(窗口缩放系数)等
这些数据都放在了TCP头部的选项部分中(12字节)
释放连接
释放连接状态
FIN-WAIT-1:表示想主动关闭连接 向对方发送了FIN报文,此时进入到FIN-WAIT-1状态
CLOSE-WAIT:表示在等待关闭 当对方发送FIN给自己,自己会回应一个ACK报文给对方,此时则进入到CLOSE-WAIT状态 在此状态下,需要考虑自己是否还有数据要发送给对方,如果没有,发送FIN报文给对方
FIN-WAIT-2:只要对方发送ACK确认后,主动方就会处于FIN-WAIT-2状态,然后等待对方发送FIN报文
CLOSING:一种比较罕见的例外状态 表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文 如果双方几乎在同时准备关闭连接的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态 表示双方都正在关闭连接
LAST-ACK:被动关闭一方在发送FIN报文后,最后等待对方的ACK报文 当收到ACK报文后,即可进入CLOSED状态了
TIME-WAIT:表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可进入CLOSED状态了 如果FIN-WAIT-1状态下,收到了对方同时带FIN标志和ACK标志的报文时 可以直接进入到TIME-WAIT状态,而无须经过FIN-WAIT-2状态
CLOSED:关闭状态
由于有些状态的时间比较短暂,所以很难用netstat命令看到,比如SYN-RCVD、FIN-WAIT-1等
释放连接过程
1、客户端:FIN=1,ACK=1,seq=j,ack=q
ESTABLISHED -> FIN-WAIT-1
2、服务端:ACK=1,seq=q,ack=j+1
ESTABLISHED -> CLOSE-WAIT 、
客户端收到后 FIN-WAIT-1 -> FIN-WAIT-2
3、服务端:FIN=1,ACK=1,seq=w,ack=j+1
CLOSE-WAIT -> LAST-ACK
4、客户端:ACK=1,seq=j+1,ack=w+1
FIN-WAIT-2 -> TIME-WAIT
服务端收到后 LAST-ACK -> CLOSED
TIME-WAIT结束 客户端 进入CLOSED
释放过程细节
TIME-WAIT 时间为 2倍MSL(Maximum Segment Lifetime,最大分段生存期)
MSL是TCP报文在Internet上的最长生存时间
每个具体的TCP实现都必须选择一个确定的MSL值,RFC 1122建议是2分钟
可以防止本次连接中产生的数据包误传到下一次连接中(因为本次连接中的数据包都会在2MSL时间内消失了)
二三次挥手可能会合并起来
补充
为什么选择在传输层就将数据“大卸八块”分成多个段,而不是等到网络层再分片传递给数据链路层?
因为可以提高重传的性能
需要明确的是:可靠传输是在传输层进行控制的
如果在传输层不分段,一旦出现数据丢失,整个传输层的数据都得重传 如果在传输层分了段,一旦出现数据丢失,只需要重传丢失的那些段即可
为什么要三次握手,为啥不能两次
防止server端一直等待,浪费资源
如果建立连接只需要2次握手,可能会出现的情况
假设client发出的第一个连接请求报文段,因为网络延迟,在连接释放以后的某个时间才到达server
本来这是一个早已失效的连接请求,但server收到此失效的请求后,误认为是client再次发出的一个新的连接请求
于是server就向client发出确认报文段,同意建立连接
如果不采用“3次握手”,那么只要server发出确认,新的连接就建立了
由于现在client并没有真正想连接服务器的意愿,因此不会理睬server的确认,也不会向server发送数据
但server却以为新的连接已经建立,并一直等待client发来数据,这样,server的很多资源就白白浪费掉了
采用“三次握手”的办法可以防止上述现象发生
例如上述情况,client没有向server的确认发出确认,server由于收不到确认,就知道client并没有要求建立连接
第3次握手失败了,会怎么处理?
此时server的状态为SYN-RCVD,若等不到client的ACK,server会重新发送SYN+ACK包
如果server多次重发SYN+ACK都等不到client的ACK,就会发送RST包,强制关闭连接
为什么要进行四次挥手
TCP是全双工模式
需要知道对方是否没有数据要发送了
为什么要有TIME-WAIT时间
如果client发送ACK后马上释放了,然后又因为网络原因,server没有收到client的ACK,server就会重发FIN
这时可能出现的情况是
client没有任何响应,服务器那边会干等,甚至多次重发FIN,浪费资源
client有个新的应用程序刚好分配了同一个端口号,新的应用程序收到FIN后马上开始执行断开连接的操作,本来 它可能是想跟server建立连接的