暂未整理,推荐阅读:小林coding-图解网络介绍
https://mp.weixin.qq.com/s/G3np-WgQu3hZNICoso4X1w
https://baijiahao.baidu.com/s?id=1654225744653405133&wfr=spider&for=pc
https://mp.weixin.qq.com/s/G3np-WgQu3hZNICoso4X1w
https://blog.csdn.net/sinat_21112393/article/details/50810053
IP层不保证网络包是否完整以及按序交付,因此需要传输层控制
TCP首部
序列号(seq):发送数据的第一个字节的序号
确认号(ack):期望收到对方下一个报文段的第一个数据字节的序号
例如序列号为301,如果数据长度是100字节,则下一个报文段的数据序号从401开始。
超过32位范围后,回到0
控制位:只占1位,0和1
- 同步SYN(Synchronization):请求建立连接
- 确认ACK(Acknowledgetment):确认已收到,TCP连接之后所有报文段ACK都为1
- 终止FIN(Finish):请求释放连接
- 紧急URG(Urgent):声明紧急指针字段是否有效
- 推送PSH(Push):接收方收到PSH信号后,立即交给应用层处理,不用等到缓冲区填满
- 复位RST(Reset):TCP连接出现错误,需要释放连接,重新建立连接。另外也用于拒绝非法报文段和拒绝连接
紧急指针:指出紧急数据位置,当URG为1时才有效。即使窗口为0,也可以发送紧急数据
首部长度:占4位,能表示的最大值为15,注意这里单位是4字节,因此最大能表示60字节的长度
选项:长度可变,最长为4字节。
固定首部为20字节,选项长度可变
x是客户端起始序列化号,y是服务端起始序列号,由发送方随机生成,没发送一次+1,用于解决包乱序问题
ack可以理解为希望对方回复的下一个序列号
TCP四元组:源地址、源端口、目标地址、目标端口
RTT
三次握手和四次挥手
三次握手
- 客户端请求连接,发送:SYN=1,seq=x
- 服务端响应连接,回复:SYN=1,ACK=1,seq=y,ack=x+1
- 客户端确认,回复:ACK=1,seq=x+1,ack=y+1
为什么需要三次握手?
- 如果是2次,服务端发出ACK后立马建立连接,此时发生拥塞客户端没有收到ACK,判断超时关闭并重新请求连接,服务端还保持着原来的连接
- 或者客户端发出SYN超时,然后重发,服务端建立连接,等到网络恢复第一个SYN到达,服务端又建立了一次连接,而客户端已经Close了
- 同步两端的序列号
四次挥手
- 客户端发送FIN,请求断开连接,客户端不再发送数据,但还能接收数据
- 服务端收到FIN包之后,回复ACK包,表示已经收到,但还需要等一下(避免客户端认为超时)
- 此时服务端数据可能还没发完,因此需要继续发送,等待服务端剩余数据发完之后,发送FIN包,告诉客户端可以关闭了
- 客户端收到FIN包之后,回复一个ACK包,告诉服务端已经收到了
- 客户端等待2MSL之后关闭
如果服务端没有数据,有可能会出现第二次挥手和第三次挥手合并的情况
为什么要四次挥手?-->需要等待服务端剩余数据传完
- 少于3次,服务端剩余数据没发送完
- 如果是3次,FIN比数据先到达,客户端可能丢失B的数据
为什么需要等待2MSL(2个报文最大存活时长)?
- B的FIN先到了,B的数据包还没发完,直接关闭的话客户端会丢失B的数据
- A发出的ACK包丢失,B会重传FIN包,如果不等待B无法正常关闭(ACK存活1MSL,B重传的FIN报文存活1个MSL)
A和B打电话
- A告诉B我有事要先撤了,你还有什么事情吗:FIN请求
- B表示好的,但是先别挂,听我说完最后一个事情:回复ACK
- B把该说的事情说了,告诉A就这些了:发送完剩余数据,请求FIN
- A说好的,那你先挂吧:回复ACK
- B听到之后就挂了:收到ACK,进入CLOSE状态
- A等B挂了之后再挂电话:等2MSL,进入CLOSE状态
TCP三次握手
PS:TCP协议中,主动发起请求的一端称为『客户端』,被动连接的一端称为『服务端』。不管是客户端还是服务端,TCP连接建立完后都能发送和接收数据。
起初,服务器和客户端都为CLOSED状态。在通信开始前,双方都得创建各自的传输控制块(TCB)。 服务器创建完TCB后遍进入LISTEN状态,此时准备接收客户端发来的连接请求。
第一次握手 客户端向服务端发送连接请求报文段。该报文段的头部中SYN=1,ACK=0,seq=x。请求发送后,客户端便进入SYN-SENT状态。
- SYN=1,ACK=0表示该报文段为连接请求报文。
- x为本次TCP通信的字节流的初始序号。TCP规定:SYN=1的报文段不能有数据部分,但要消耗掉一个序号。
第二次握手 服务端收到连接请求报文段后,如果同意连接,则会发送一个应答:SYN=1,ACK=1,seq=y,ack=x+1。 该应答发送完成后便进入SYN-RCVD状态。
- SYN=1,ACK=1表示该报文段为连接同意的应答报文。
- seq=y表示服务端作为发送者时,发送字节流的初始序号。
- ack=x+1表示服务端希望下一个数据报发送序号从x+1开始的字节。
第三次握手 当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示:服务端发来的连接同意应答已经成功收到。 该报文段的头部为:ACK=1,seq=x+1,ack=y+1。 客户端发完这个报文段后便进入ESTABLISHED状态,服务端收到这个应答后也进入ESTABLISHED状态,此时连接的建立完成!
为什么连接建立需要三次握手,而不是两次握手? 防止失效的连接请求报文段被服务端接收,从而产生错误。
TCP四次挥手
第一次挥手 若A认为数据发送完成,则它需要向B发送连接释放请求。该请求只有报文头,头中携带的主要参数为: FIN=1,seq=u。此时,A将进入FIN-WAIT-1状态。
- FIN=1表示该报文段是一个连接释放请求。
- seq=u,u-1是A向B发送的最后一个字节的序号。
第二次挥手 B收到连接释放请求后,会通知相应的应用程序,告诉它A向B这个方向的连接已经释放。此时B进入CLOSE-WAIT状态,并向A发送连接释放的应答,其报文头包含: ACK=1,seq=v,ack=u+1。
- ACK=1:除TCP连接请求报文段以外,TCP通信过程中所有数据报的ACK都为1,表示应答。
- seq=v,v-1是B向A发送的最后一个字节的序号。
- ack=u+1表示希望收到从第u+1个字节开始的报文段,并且已经成功接收了前u个字节。
A收到该应答,进入FIN-WAIT-2状态,等待B发送连接释放请求。
第二次挥手完成后,A到B方向的连接已经释放,B不会再接收数据,A也不会再发送数据。但B到A方向的连接仍然存在,B可以继续向A发送数据。
第三次挥手 当B向A发完所有数据后,向A发送连接释放请求,请求头:FIN=1,ACK=1,seq=w,ack=u+1。B便进入LAST-ACK状态。
第四次挥手 A收到释放请求后,向B发送确认应答,此时A进入TIME-WAIT状态。该状态会持续2MSL时间,若该时间段内没有B的重发请求的话,就进入CLOSED状态,撤销TCB。当B收到确认应答后,也便进入CLOSED状态,撤销TCB。
TCP和UDP
UDP | TCP | |
---|---|---|
是否连接 | 无连接 | 面向连接 |
是否可靠 | 不可靠传输,不使用流量控制和拥塞控制 | 可靠传输,使用流量控制和拥塞控制 |
连接对象个数 | 支持一对一,一对多,多对一和多对多交互通信 | 只能是一对一通信 |
传输方式 | 面向报文 | 面向字节流 |
首部开销 | 首部开销小,仅8字节 | 首部最小20字节,最大60字节 |
适用场景 | 适用于实时应用(IP电话、视频会议、直播等) | 适用于要求可靠传输的应用,例如文件传输 |
- TCP在传输数据时,如何保证数据的可靠性
- 连接管理:三次握手、四次挥手
- 校验和:校验数据是否损坏
- 序列号:用于检测丢失的分组(丢失数据)和冗余的分组(重传数据)
- 确认应答:接收方告知发送方正确接收分组以及期望的下一个分组,否定确认(NACK,Negative-Acknowledgment):接收方通知发送方未被正确接收的分组
- 流量控制:滑动窗口
- 拥塞控制:网络拥塞时减少数据发送
- 累积确认:多个数据包,回复一次确认。避免网络充斥大量确认回复报文
- 选择确认SACK:选项字段,一个报文段(多个报文)使用两个边界确定,选项字段有4个字节,左右边界各4位,因此最多可以表示4个已接受的报文段
- 自动重传请求(ARQ,Automatic Repeat-Request):
- 窗口和管道(用于增加信道的吞吐量)
局域网内两台主机,一台主机去ping另一台主机的IP,其过程
- 路由器ARP找到对应的主机MAC
Android长连接的心跳机制:如何设计心跳时间,考虑哪些因素DHCP租期. NAT超时. 网络切换. 如何保证尽可能大的心跳时间
- UDP如何实现丢包重传策略?
Stop Wait:等待响应之后再发送下一个请求
重传机制
- A发送数据包之后开始计时,超时没有收到确认回复,则认为发生丢包,再次发送
- 前一个数据包超时,但能到达,重传之后会存在两个一样的数据包,接收方根据序列号判断属于重传数据还是新数据
随网络环境动态变化
RTT(Round-Trip Time,往返时延):数据完全发送完到收到确认信号的时间。
RTT是动态的,重传时间每次RTTx2,到达一定阈值之后认为连接已断开。
重传时间=2*RTT,RTT = previousRTT * i + (1 - i) * currentRTT
,i通常为90%,即新的RTT是以前的RTT值的90%加上当前RTT值的10%.
定时器(分组丢失则重传)
- 重传计时器(Retransmission):超时之前收到则取消计时,超时之后则重传数据,并复位计时器。
- 坚持计时器(Persistent Timer):当窗口大小为0时,启动计时,截止时发送探测报文段,有序号,但不需要确认。窗口为0之后过一段时间服务端会发送一个非0窗口大小的ACK用于更新窗口,但可能丢失,因此需要使用坚持计时器
- 保活计时器(Keeplive Timer):服务器每次收到客户信息,复位计时器,超时时间通常为2小时。连接建立完成之后一直没有数据,导致服务端一直保持该连接。
- 时间等待计时器(Time_Wait Timer):四次挥手之后客户端进入Time Wait状态,通常为2MSL
流量控制
接收方来不及处理发送方的数据,提示发送方降低速率,防止包丢失
拥塞控制
网络拥塞时,减少数据发送
慢启动阈值(ssthresh,slow start thresh):处理拥塞的参照值(不是真正发生了拥塞),随着实际网络情况变化。
拥塞窗口(cwnd,Congestion Window)
- 慢开始:当cwnd < ssthresh时,cwnd以慢开始方式指数增长x2,设置较小的初始值
- 拥塞避免:当cwnd >= ssthresh时,cwnd以拥塞避免的方式线性增长+1,试探网络底线
- 发送超时或者收到三个相同的ACK确认回复,表示网络拥塞,需要降低ssthresh阈值
- 快重传:收到丢包信息,客户端立即重传,不等到超时
- 快恢复:cwnd不是从1重新开始,而是从ssthresh开始继续拥塞避免
滑动窗口
客户端根据拥塞窗口和服务端接收窗口确认发送窗口大小:swnd=min(rwnd, cwnd)
接收端每次响应携带可用窗口大小
发送窗口(swnd,Send Window)
- 已发送并收到确认
- 已发送但未收到确认
- 等待发送
- 不允许发送
接收窗口(rwnd,Receive Window):收到窗口外的数据则丢弃
- 已回复确认,但是还没被应用层接收
- 已经接收但是还没回复ACK
- 允许接收区段
- 不允许接收区段
其他问题
分块传输
数据包为什么要分成多个报文传输,而不是加上首部一次传输?
数据链路层限制数据长度只能有1460。
- 如果一次传输太多数据,失败之后需要重传整个数据包
- 如果数据包拆分太细,会使得首部占比过大。
路由转发
发送端到接收端存在多条路径。
- 提高网络容错率:当中间某个路由坏了,可以走其他路径
- 分流:当某一条线路拥挤,可以走其他路径
粘包和拆包
TCP从应用层拿到数据流,拆分成多个报文发送。TCP并不知道数据流是否来源于同一数据包。
- 粘包:两个文件的数据混在一起,形成一个报文
- 拆包:接收端收到报文之后,需要将数据拆开
解决思路:
- 添加特殊的结束字符,接收端根据分割字符分离数据
- 控制每个报文只包含一个文件数据,不足的用0填充
恶意攻击
客户端伪造IP同时发出大量请求,服务端维持大量无用的TCP连接或缓冲区,导致无法响应请求。
长连接
请求完之后不关闭TCP连接,避免每次请求都新建一个TCP连接。