请求是怎么到达对方主机?
1. OSI开放系统互联
定义了网络框架,以层为单位实现协议,同时控制权逐层传递。
OSI实际并没有落地,TCP/IP 5层协议栈是目前主流的落地实现。
1.1. TCP/IP 5层协议栈
TCP/IP协议栈不止是传输层tcp/网络层ip, 还包括应用层等,这是一个协议簇,只是因为TCP/IP很具代表性。
不管是OSI还是TCP/IP5层协议栈,均会出现应用程序和操作系统边界(代码执行在用户态/内核态)。
边界调用被称为系统调用system call, socket 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粘包/拆包问题, 需要应用层用特殊分隔符或者长度解析。
本文来自博客园,作者:{有态度的马甲},转载请注明原文链接:https://www.cnblogs.com/JulianHuang/p/17386121.html
欢迎关注我的原创技术、职场公众号, 加好友谈天说地,一起进化