请求是怎么到达对方主机?

1. OSI开放系统互联

定义了网络框架,以层为单位实现协议,同时控制权逐层传递。

OSI实际并没有落地,TCP/IP 5层协议栈是目前主流的落地实现

1.1. TCP/IP 5层协议栈

TCP/IP协议栈不止是传输层tcp/网络层ip, 还包括应用层等,这是一个协议簇,只是因为TCP/IP很具代表性。

不管是OSI还是TCP/IP5层协议栈,均会出现应用程序和操作系统边界(代码执行在用户态/内核态)。

边界调用被称为系统调用system callsocket api便是TCP/IP协议栈中应用层的网络编程接口。

1.2. 请求是如何到达对方主机?

tcp/ip协议栈 对应tcpdump左下视图
应用层
传输层 TCP、UDP Transmission Control Protocol
网络层 (IP,ICMP,ARP协议) Internet Protocl Version 4
链路层 (以太网协议) Ethernet2
  • 应用层对请求包做格式定义, 比如http;
  • 传输层协议加上 双方通信的端口号,确定 通信双方的应用程序;
  • 网络层加上双方的IP地址(可能由DNS协议给出), 确定了双方的网络位置;
  • 最后链路层加上了 MAC地址(通过ARP协议),确认双方物理位置,并对数据包分组,形成数据帧Frames, 最后通过传输介质送到对方主机。

当源主机和目标主机不在同一个网络(子网)内时,源主机会将数据包发送到其默认网关(通常是路由器)的 MAC 地址。这是因为默认网关负责将数据包转发到其他网络。

以下是详细的工作流程:

1>. 确定目标网络

  • 源主机首先检查目标 IP 地址是否在同一个子网内。这是通过比较源主机的子网掩码和目标 IP 地址来完成的。

2>. 选择默认网关

  • 如果目标 IP 地址不在同一个子网内,源主机会选择将数据包发送到其默认网关。

3>. ARP 请求

  • 源主机使用地址解析协议(ARP)来确定默认网关的 MAC 地址。它会在本地网络上广播一个 ARP 请求,询问谁拥有默认网关的 IP 地址。
  • 默认网关响应这个请求,提供它的 MAC 地址。

4>. 封装数据包

  • 源主机将数据包的目标 MAC 地址设置为默认网关的 MAC 地址,而目标 IP 地址保持为最终目标的 IP 地址。
  • 数据包被封装在一个以太网帧中,并通过本地网络发送到默认网关。

5>. 路由转发

  • 默认网关接收到数据包后,根据其路由表,决定如何将数据包转发到目标网络。这可能涉及多个路由器的转发,直到数据包到达目标网络。

6>. 最终到达目标主机

  • 一旦数据包到达目标网络,最后一个路由器会使用 ARP 找到目标主机的 MAC 地址,并将数据包发送给目标主机。

这个过程确保了即使源主机和目标主机不在同一个网络中,数据包也能够通过网络设备(如路由器)进行有效传输。

2.TCP/UDP概览

  • TCP: Transmission Control Protocol面向连接的,可靠的,基于字节的、双向流式传输层协议。

  • UDP: USer Datagram Protocol面向消息的传输服务,传输的数据是有边界的。
    区别:

TCP可靠性是tcp三次握手的基础,在此之上,增加了seq、ack数据确认机制、 拥塞控制, 其中ack= seq+len(data)。

UDP: 想法就发,不用三次握手建立连接。

我们目前常见的应用场景底层都是tcp,比如http请求、sql数据库请求。

建立TCP连接之后,才能做http请求、sql请求,tcp连接很耗时,故服务器都存在连接池化机制。

这里我要给自己强调的是:开发者对于tcp一定不要带入http请求-响应模型,tcp是双向通信流。

3. TCP粘包/拆包

TCP粘包并不是TCP协议造成的问题,因为tcp协议本就规定字节流式传输

  • 正常的理想情况,应用层下发的两个原始包恰好满足TCP缓冲区的大小或达到TCP等待时长,分别发送两个包;
  • 粘包:两个包较小,间隔时间短,发生粘包,合并成一个包发送;
  • 拆包:一个包过大,超过缓存区大小,拆分成两个或多个包发送;
  • 拆包和粘包:Packet1过大,进行了拆包处理,而拆出去的一部分又与Packet2进行粘包处理。

粘包拆包问题在数据链路层、网络层以及传输层都有可能发生。
数据链路层,网络层的粘包和拆包问题都由协议自行处理了,我们日常的网络应用开发都在对接传输层,故面临的粘包问题指的是TCP粘包。


当粘包、拆到TCP层的时候我们就没办法识别应用层的请求/调用了, 所以解决方法是:一开始就需要在字节流中加入[特殊分隔符]或者[长度+偏移量]含义。

HTTP 超文本传输协议的规定如下:

4. IO操作的耗时

上面已经引出了tcp内核缓存冲, 当缓冲区满的时候,上层若要再写需要等待; 缓冲区为空时,上层若需要再读需要等待

从应用层角度, IO操作一般划分为两个阶段:

  • 等待缓冲区有空隙, 可以继续发送数据; 等待缓冲区有数据,可以读取数据, 而这个等待时间是不定的, 你不知道对端什么时候拿数据让本端缓冲区有空隙, 也不知道对端什么时候向本端发送数据;
  • 经过事件通知机制(select poll epoll等)给到应用层, 主线程/IO线程将数据从内核缓冲区拷贝进 内存缓冲区。

长耗时的是第一阶段。

5. 旁白

梳理了整个TCP/IP协议栈各层封包逻辑, 我们就知道粘包、拆包一直都存在,只是拆到TCP层的时候,我们没有办法区分应用层断续发送的请求/调用, 这就是我们口口相传的TCP粘包/拆包问题, 需要应用层用特殊分隔符或者长度解析。

posted @ 2023-05-09 20:08  码甲哥不卷  阅读(534)  评论(0编辑  收藏  举报