【网络通信】TCP知识扫盲(报文解析+三四握手+TCP粘包+TCP攻击)

1 TCP报文格式

序号:seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
确认序号:ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1
标志位:共6个,长度刚好6位,值0/1,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:

  • URG:紧急指针(urgent pointer)有效。
  • ACK:确认序号有效。
  • PSH:接收方应该尽快将这个报文交给应用层,立即读取数据不等缓冲区满。
  • RST:重置连接。
  • SYN:发起一个新连接。
  • FIN:释放一个连接。

可靠性体现

  • 每收到对端数据(SYN,FIN,DATA),就发送 ACK 进行确定,发送方发送后没有收到 ACK 就隔一段时间重发数据
  • 发送序号值为x,响应的ACK序号值为=x+1(或者是x+DATA数据长度),以此保证请求与响应的映射关系
  • 重发数据时,序号值不变,如果服务端接收到重复数据可以忽略,仅返回ACK表示确认收到数据(用于发送端由于网络原因没有接受到ACK,导致重发数据的情况)

2 三次握手与四次挥手

2.1 建立连接-三次握手

  • 第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
  • 第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
  • 第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。

通俗理解:为何需要三次握手?

  • 第一次握手:Client想确认自己往Server端发数据是否有问题?Server端接受数据是否有问题?
  • 第二次握手:Server告诉Client你发送数据没毛病,我接受数据也没毛病,同时想确认Client是否能够正确接收Server端发的数据。
  • 第三次握手:Client确认了自己发送数据,接受数据没问题,且对端接受数据,发送数据也都没问题,这时候Client放心的把这个结果告诉Server端,我们之间一切OK。然后就可以正常通信了。

总结来说:就是一个确认双方是否可以正常接受数据,发送数据的过程。

2.2 断开连接-四次挥手

由于TCP连接是全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来告知对方不会再收到数据了,但是在这个TCP连接上对方可能还在发送数据。所以,FIN和ACK,不能一起返回,期间需要等待数据传输结束,TCP毕竟是安全可靠的数据传输协议,这也是为何需要四次挥手的原因。

  • 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
  • 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
  • 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
  • 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态(2MSL),接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

问题:为何第四次挥手后Client要进入TIME_WAIT状态?为何是2MSL(最大报文段生存时间)

避免最后一个ACK报文因为网络原因被丢弃,此时server因为没有收到ACK而超时重传FIN报文,处于TIME_WAIT状态的client可以继续对FIN报文做回复,向server发送ACK报文。2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。如果再次接受到FIN,那么又将继续等待2MSL。

通俗理解:为何要四次挥手?

  • 第一次挥手:Client告诉Server我不想连接了。
  • 第二次挥手:Server收到消息后,回应Client,我知道了,等我最后的消息发送完,我们就拜拜,再等等。
  • 第三次挥手:Server告诉Client,我数据发完了,我们可以拜拜了。
  • 第四次挥手:Client收到消息后,回应Server,我知道了,你先关闭连接,我会确认你收到我的消息后,我再关闭。

同时挥手

分析:其实就是一个双方发送FIN,再响应ACK,最后等待确认对方收到ACK的过程。

TPC全生命周期:

问题:如果已经建立了连接,但是客户端突然出现故障了,咋办?

TCP有一个保活计时器,服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

PS:查看当前系统所有TCP连接状态:

# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
SYN_RECV 10
CLOSE_WAIT 62
ESTABLISHED 3916
CLOSING 13

3 TCP粘包拆包

TCP(transport control protocol,传输控制协议)是个流协议,无消息保护边界,它是根据TCP缓冲区的实际情况进行包划分,而不管上层业务划分,所以可能出现一个大包被拆分成多个小包,也可能出现多个小包被合并成一个大包发送。

那如何解决这个问题呢?

很简单,没有边界我们就定义一个边界,然后按照边界规则去解析,比方说换行。常常我们会使用BufferReader的readline去读取TCP消息,其实就是悄咪咪的自定义了读取边界为换行。

UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的。不会使用块的合并优化算法,由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 有消息保护边界的。

4 TCP攻击

  • Ping of Death
ping -l 6549900 192.168.1.1 -t

原理:发超出IP协议限制的数据包时,数据包会被拆分成多个发送,接收端需要将所有分包接受完成才能确认总包大小,此时,所有数据分包存放在缓存区等待重组,频繁发送可能导致溢出。也可以手动伪造分包。

预防:打补丁,升级。

  • SYN Flood

原理:只发SYN包给服务端,不发ACK,导致服务端无法完成三次握手而反复发送SYN+ACK(第二次握手数据)直到超时

预防:缩短SYN Timeout时间;检测此类攻击,直接对该IP进行拒绝访问

  • Smurf

原理:发送伪装的ICMP数据包,目的地址设为某个网络的广播地址,源地址设为要攻击的目的主机,使所有收到此ICMP数据包的主机都将对目的主机发出一个回应,使被攻击主机在某一段时间内收到许多无效的数据包

预防:在cisco路由器上配置来防止将包传递到广播地址上:Router(config-if)# no ip directed-broadcast

  • teardown

原理:依旧是篡改大包的分包,将分包的偏移量进行篡改,导致接收端无法正确组装出完整的数据包,不断的无效尝试会浪费系统资源

  • LandAttack

原理:利用一个特别打造的SYN包--它的原地址和目标地址都被设置成某一个服务器地址进行攻击。此举将导致接受服务器向它自己的地址发送SYN-ACK消息,结果这个地址又发回ACK消息并创建一个空连接,每一个这样的连接都将保留直到超时

  • IP欺骗

原理:利用TCP协议栈的RST位来实现,使用IP欺骗,迫使服务器把合法用户的连接复位,影响合法用户的连接。就是伪装成正式用户告诉服务器要重置连接,之后正式用户再请求,则会被拒绝服务而重新开始建立新的连接。

  • DOS

原理:方式方法很多,本质就是构造无效数据包(数据量大或连接数多),搞垮服务器,以上多种攻击手段都可以被利用到DOS上。


爱家人,爱生活,爱设计,爱编程,拥抱精彩人生!

发布了96 篇原创文章 · 获赞 237 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qqchaozai/article/details/100082846