Network FAQ
Network FAQ
Network Model
四层模型
- 网络接口层:相当于OSI模型的物理层和数据链路层,负责处理物理硬件(MAC) 地址和网络接口的细节。
- 网络层:相当于OSI模型的网络层,负责处理IP地址和路由。
- 传输层:相当于OSI模型的传输层,处理TCP和UDP协议。
- 应用层:相当于OSI模型的会话层、表示层和应用层,处理高级应用协议和用户交互。
七层架构
- 物理层:负责处理电气和物理规范,如电压、线路规范、数据速率、最大传输距离等。
- 数据链路层:负责从物理层接收的原始比特流转换为数据帧,进行物理地址(MAC)寻址,同时也处理错误检测和校正。
- 网络层:负责数据包的发送和接收,包括IP地址处理和路由。
- 传输层:负责提供端到端的通信服务,如数据的分段、传输和重组,以及错误检测和纠正。TCP和UDP都工作在这一层。
- 会话层:负责建立、管理和终止会话。
- 表示层:负责数据的转换、加密和解密、压缩和解压缩等。
- 应用层:负责提供网络服务,如HTTP、FTP、SMTP等协议都工作在这一层。
FAQ
在 TCP 上来说,对于运营商来说,如果客户端没有发起请求,服务器能够直接给客户端终端发数据包吗?理论上能实现吗?
- TCP(传输控制协议)和 UDP(用户数据报协议)是传输层协议,它们处理的是数据包的传输,而不是具体的请求和响应。
- Request / Response 是 HTTP 的规范,不是 TCP 的规范。
- 从网络层的角度看,服务器可以向任何IP地址发送数据包
- 如果服务器发送数据包到一个未建立TCP连接的客户端,那么客户端的网络层确实可以收到这个数据包。
- 但是在更高的传输层,由于没有对应的TCP连接,客户端会认为这是一个无效的数据包,所以会丢弃它,而不是处理它。
- 如果TCP连接已经建立,那么服务器是可以主动发送数据给客户端的。
- 家庭 NAT 或者 终端的网络防火墙可能会拦截这类数据包。
在UDP上来说,如果客户端没有发起请求,服务器能够直接给客户端发数据包吗?理论上能实现吗?
- 在UDP协议中,服务器确实可以直接向客户端发送数据包,即使客户端没有发起请求。
- 因为UDP是无连接的,即它不需要预先建立一个连接才能发送数据。只要服务器知道客户端的IP地址和端口号,它就可以向客户端发送数据。
- 如果客户端没有预期接收到这个数据包,那么它可能会忽略这个数据包,或者如果它正在使用一个防火墙或其他安全措施,它可能会把这个数据包视为未经请求的入侵尝试,并将其丢弃。
- 此外,一些网络设备,如NAT(网络地址转换)设备,可能会阻止未经请求的入站数据包到达客户端。这是因为NAT设备通常只允许从内部网络发起的数据流通过,并且只允许来自这些数据流的响应数据包返回。即家庭 NAT 或者 终端的网络防火墙可能会拦截这类数据包。
- 这就是为什么在许多情况下,客户端需要首先向服务器发送一个请求,以"打开"一个可以让服务器的响应返回的路径。
UDP
限流
运营商或ISP(Internet Service Provider)可以通过多种方式来限制(或塑形)UDP或其他类型的网络流量。以下是一些常见的方法:
- 带宽限制:ISP可以限制特定类型的流量的总带宽,
- 例如将所有UDP流量限制在一定的速率内。
- 这可以通过监控传入和传出的数据包,并丢弃或延迟超过限制的数据包来实现。
- 深度包检查(Deep Packet Inspection, DPI):DPI是一种网络数据包过滤方法,可以识别、分类和处理网络流量。
- ISP可以使用DPI来识别特定类型的UDP流量(例如特定的应用程序或服务),然后对这些流量进行限制。
- 流量整形(Traffic Shaping):ISP可以使用流量整形技术,例如漏桶(Leaky Bucket)或令牌桶(Token Bucket)算法,来控制网络流量的速率。
- 这些算法可以平滑网络流量,并限制突发流量。
- 优先级队列(Priority Queuing):ISP还可以使用优先级队列技术
- 根据数据包的优先级(例如服务质量QoS标记)来处理数据包。
- 低优先级的流量可能会被延迟或丢弃,以确保高优先级流量的传输。
FAQ
通过丢包的方式能够限流UDP的流量吗?
是的,通过丢弃数据包的方式,运营商可以限制UDP的流量。这种方法通常被称为"流量整形"或"流量控制"。
这种丢包的方法可以有效地限制UDP流量,但也有一些缺点。
- 首先,丢弃数据包可能会导致应用性能下降,尤其是对于需要高质量数据传输的应用(如视频流)。
- 其次,UDP本身没有提供任何的错误恢复机制,所以一旦数据包被丢弃,发送方不会知道,也无法重新发送这些数据包。
TCP
- TCP是互联网协议套件的核心协议之一,提供可靠、有序和错误检查的数据传输。
- TCP连接确保数据包按照正确的顺序到达,并且没有丢失。
TCP 连接是什么
简单来说就是,用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接。
所以我们可以知道,建立一个TCP连接是需要客户端与服务端达成上述三个信息的共识。
- Socket:由IP地址和端口号组成
- 序列号:用来解决乱序问题等
- 窗口大小:用来做流量控制
如何唯一确定一个 TCP 连接呢?
TCP 四元组可以唯一的确定一个连接,四元组包括如下:
- 源地址
- 源端口
- 目的地址
- 目的端口
三次握手
- SYN步骤:客户端发送一个数据包给服务器端,数据包中包含客户端的初始序列号。这个步骤将客户端的序列号设置为X。
- SYN + ACK步骤
- 服务器收到客户端的SYN包,需要对这个SYN包进行确认,此时,服务器会设置确认号为X+1。
- 同时,服务器也会发送一个SYN请求信息,且会把自己的初始序列号设置为Y。
- ACK步骤:客户端收到服务器的SYN+ACK包,会向服务器发送确认包ACK。确认包的确认号为Y+1,序列号为X+1。
为什么三次
我们的目标是,为了确保可靠,我们需要让客户端终端和服务端同时确认对方都能够接受到自己的消息。
- 说法一:三次握手的主要目标是同步双方的初始序列号,以此建立可靠的连接。
- 说法二:TCP使用三次握手来创建一个可靠的连接,这是为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
假设我们只进行一次或者两次握手,可能会出现以下问题:
一次握手
如果只进行一次握手,客户端不知道连接是否已经建立。后续的任何通信都是不可靠的。
两次握手
即使进行两次握手,也存在问题。
- 服务器确认:服务器能收到消息
- 客户端确认:服务器能收到消息,客户端能够收到消息
服务器不知道自己的消息是否被客户端接收。
四次挥手
双方都可以主动断开连接,断开连接后主机中的「资源」将被释放,四次挥手的过程如下图:
你可以看到,每个方向都需要一个FN和一个ACK,因此通常被称为四次挥手。这里一点需要注意是:主动关闭连接的,才有TIME_WAIT状态。
- 关闭连接时,客户端向服务端发送FIN时,仅仅表示客户端不再发送数据了但是还能接收数据。
- 服务端收到客户端的 FIN报文时,先回一个 ACK应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。
在一些情况下, TCP 四次挥手是可以变成 TCP 三次挥手的。
而且在用 wireshark 工具抓包的时候,我们也会常看到 TCP 挥手过程是三次。
服务器收到客户端的FN报文时,内核会马上回一个ACK应答报文,但是服务端应用程序可能还有数据要发送,所以并不能马上发送FIN报文,而是将发送FIN报文的控制权交给服务端应用程序。
- 如果服务端应用程序有数据要发送的话,就发完数据后,才调用关闭连接的函数;
- 如果服务端应用程序没有数据要发送的话,可以直接调用关闭连接的函数。
从上面过程可知,
- 是否要发送第三次挥手的控制权不在内核,而是在被动关闭方(通常是服务器) 的应用程序
- 因为应用程序可能还有数据要发送,由应用程序决定什么时候调用关闭连接的函数,当调用了关闭
连接的函数,内核就会发送FN报文了,所以服务端的 ACK 和 FIN 一般都会分开发送。
FAQ
如果客户使用 跳板机,客户终端的 TCP 连接会不会被跳板机的连接超时机制掐断?
跳板机 or 虚拟专用网络服务 通常对传输的数据类型(例如 TCP 或 UDP)是透明的。
也就是说,无论你是使用 HTTP、HTTPS、WebSocket 还是其他任何基于 TCP 或 UDP 的协议,跳板机 or 虚拟专用网络服务 都应该能够正确地传输数据。
然而,一些 跳板机 or 虚拟专用网络服务 可能会设置连接超时机制,也就是说,如果在一段时间内没有数据传输,跳板机 or 虚拟专用网络服务可能会自动断开连接。
这可能会影响到 WebSocket,因为 WebSocket 是一种长连接,即使在没有数据传输的时候,连接也会保持开启。
你可以过定期发送一些小的数据包(通常被称为 "心跳" 或 "ping/pong" 消息)来保持连接活跃。这可以防止 跳板机 or 虚拟专用网络服务 因为没有数据传输而断开连接。
HTTP
HTTP(超文本传输协议)
- HTTP是用于传输超媒体文档(如HTML)的应用层协议。它是万维网的数据通信的基础。
- 在 HTTP 协议(这是建立在 TCP 之上的应用层协议)中,响应(response)是对请求(request)的回应。
- HTTP是一种无状态协议,这意味着每个请求都是独立的,服务器不会保留之前请求的信息。
**在 HTTP 协议中,通信流程通常是由客户端发起的请求(request),然后服务器返回一个响应(response)。**在没有请求的情况下,服务器不能发送 HTTP 响应。如果服务器在没有请求的情况下发送了 HTTP 响应,那么客户端可能会将其视为无效数据并丢弃。
- 在 HTTP/1.0 和 HTTP/1.1 中,每个请求都对应一个响应。
- 在 HTTP/2 中,一个单一的 TCP 连接可以并行处理多个请求和响应,但仍然是每个请求对应一个响应。
TCP 连接复用
TCP 连接复用通常是指在 HTTP/1.1 中使用的 keep-alive 机制。
- HTTP/1.1 引入了持久连接(Persistent Connections),也就是 keep-alive,使得同一个 TCP 连接可以被多个请求和响应复用,而不必为每个请求都建立一个新的连接。
- 这减少了连接建立和拆除的开销,但在同一个连接上,多个请求和响应还是串行处理的,无法实现真正的并行。
Multiplexing
在 HTTP/2 中,单一的 TCP 连接可以并行处理多个请求和响应,这个特性被称为“多路复用”(Multiplexing),而不是简单的 TCP 连接复用或 keep-alive。
多路复用是 HTTP/2 的一个核心特性,它允许在一个单一的 TCP 连接上并行发送和接收多个 HTTP 请求和响应。
- 每个请求和响应都被分割成更小的帧,这些帧可以交错发送,并在接收端重新组装。
- 这大大提高了传输效率,减少了延迟和阻塞。
HTTP/2 通过多路复用解决了 HTTP/1.1 中的队头阻塞(Head-of-Line Blocking)问题,使得多个请求和响应可以并行进行,大大提高了网页加载速度和资源利用效率。
总结
- Keep-Alive 是 HTTP/1.1 的一个机制,用于保持 TCP 连接打开,减少连接建立和关闭的开销。
- TCP 连接复用 通常指的是 HTTP/1.1 中的 keep-alive 机制,允许在一个连接上处理多个请求和响应,但这些请求和响应是串行的。
- 多路复用(Multiplexing) 是 HTTP/2 的一个特性,允许在一个 TCP 连接上同时处理多个请求和响应。
所以,HTTP/2 的特性是多路复用,而不是简单的 TCP 连接复用或 keep-alive。
Streaming
HTTP 流式返回(Streaming),支持客户端发送一个请求,然后服务器通过该连接持续发送多个响应 (一次请求,多次响应)。
- 服务器向客户端传输数据的方式允许数据分块发送而不是一次性发送完毕。
- 这样客户端可以在接收到第一部分数据时就开始处理,而不必等待整个响应完成。
这种技术允许服务器在同一连接上连续地发送数据更新,而无需客户端反复发送新的请求。这种机制在需要实时数据推送的应用中非常有用。
区分 HTTP Streaming 和 HTTP Live Streaming (HLS)
- HTTP Streaming 的用例有 ChatGPT 的流式回复,客户可以直接得到答案,并等待后续的文本补全。
- HTTP Live Streaming 是 苹果公司发布的一个视频传输协议,也是 HTTP Streaming 的一种实现。
工作原理
- 客户端发送请求:客户端向服务器发送一个初始的 HTTP 请求,这个请求可以是一个普通的 GET 请求。
- 服务器保持连接:服务器接收到请求后,不会立即关闭连接,而是保持这个连接打开。
- 服务器发送响应数据:服务器通过这个打开的连接持续发送数据块,每个数据块可以被视为一个“部分响应”。这些数据块可以包含新的数据更新,服务器会根据需要发送这些数据块。
- 客户端处理数据:客户端会持续监听这个连接,并处理服务器发送的每个数据块。这种方式使得客户端可以接收到实时的数据推送。
Chunked Transfer Encoding
HTTP Streaming 可以利用 Chunked Transfer Encoding 来实现。
- Chunked Transfer Encoding 是 HTTP/1.1 引入的一种传输编码方式,允许服务器以一块一块的形式发送响应数据,而不需要在发送之前知道响应的总大小。
- 这种编码方式通过在每个数据块前面添加一个表示块大小的十六进制数字来实现。
- 每个数据块后面紧跟一个 CRLF(回车换行)序列,最后一个数据块大小为 0,表示传输结束。
HTTP/2 中, 多个 Stream 复用一条 TCP 连接,可以达到并发的效果。解决了 HTTP/1.1 队头阻塞的问题,提高了 HTTP 传输的吞吐量。
提示
虽然 HTTP/1.1 支持服务器流(Streams),但它仍然是基于 HTTP 的请求/响应模型的。
服务器推送允许服务器在客户端请求前预先发送数据,但这仍然需要一个初始的客户端请求。
- 流允许在单个 TCP 连接上并行处理多个请求和响应,但每个流仍然是基于请求/响应的。另一方面
- WebSocket 是全双工的,一旦连接建立,服务器和客户端都可以随时发送数据。
运营商级 NAT
运营商级NAT(Carrier-Grade NAT,CGNAT),是一种网络地址转换(NAT)技术,通常由互联网服务提供商(ISP)在其网络中实施。
它旨在解决IPv4地址短缺的问题,并允许 ISP 在其网络中共享一组公共 IPv4地址,以为其订阅者提供 Internet 访问。
CGNAT 的 主要作用是将多个内部IP地址映射到较少数量的公共 IP 地址上,从而允许多个用户共享同一组IP地址访问互联网。其目的是延缓IPv4地址枯竭的影响,并为ISP提供在IPv4和IPv6之间过渡的机会,同时提供Internet连接给大量的订阅者。
原理
CGNAT的工作原理类似于传统的网络地址转换(NAT),但它是在运营商的网络层面上实施的。
- 当用户设备连接到运营商的网络时,运营商分配给用户的是一个私有IP地址,通常是在RFC 1918定义的地址范围内(如192.168.x.x、10.x.x.x等)。
- 当用户设备尝试访问 Internet上的资源时,数据包将首先到达运营商的CGNAT设备。
在CGNAT设备上,
- 发送包时,会对数据包的源IP地址和端口进行修改,将其转换为一个公共IP地址和端口,并记录,然后将数据包转发到互联网上。
- 当响应数据包返回时,CGNAT设备会根据预先建立的映射关系,将目标IP地址和端口转换为原始的私有IP地址和端口,然后将响应数据包转发回用户设备。
CGNAT 会保留特殊端口,如 80, 443, 3389 等,不将其用于映射。
局限性
由于CGNAT会修改数据包的源IP地址和端口,可能会导致一些应用程序或服务出现问题,比如P2P连接、IP地址依赖的应用等。
P2P 阻拦
为什么绝大多数地区的,两个不同家庭网络之间的设备终端之间(P2P),无法通过 IPv4 通信?因为 CGNAT 的存在,把家庭网络拿到的IP变成了内网IP。
- 直接使用公网IP通信,只适用于两个拥有公网IP(且无NAT映射)的设备。
- Internet 上具有公网IP地址的主机不能主动访问 NAT 之后的主机
- 而,位于不同NAT之后的主机之间更是无法相互识别因而不能直接交换信息。
- 此外,运营商可能在运营商的交换机或者家庭光猫上部署了防火墙等,来丢弃某些特定模式的包。
现在运营商已经破坏了传统的网络通信模型,如P2P;设计方案基本只围绕 HTTP over TCP 的设计模式,即 request - response。
这给处于不同内网中的主机进行 P2P 通信带来了障碍,限制了 P2P 的应用。因此,要在目前的网络环境中进行有效的P2P通信,就必须研究相应的方案来穿越NAT 。
笔者的见解
笔者自小学就开始折腾公网 IP 的问题。之前一直天真的认为 P2P 不通是因为运营商侧加了防火墙,限制必须是家庭网先发出数据包后才能建立连接。
后来得知这是错误的。自 IPv6 开始普及后,IPv6 没有走NAT,P2P 能够直接打通。直到今天我才弄明白了这个问题。
NAT 穿透
针对网络中的NAT穿越问题,目前业界主要有如下解决方案:ALG方式、MII3COM方式、STUN方式、TURN方式、ICE方式、Full Proxy方式等。
Websocket
TCP 连接的两端,同一时间里,双方都可以主动向对方发送数据。这就是所谓的全双工。
WebSocket 是一种通过 TCP 连接在两台计算机之间提供全双工通信通道的协议。
- 设计用于在 Web 浏览器 和 Web 服务器 中实现
- 通信通过 TCP 端口 80 完成(可用于安全计算环境)
HTTP vs WebSocket
HTTP是半双工的:一次只能有一方发送数据,就像使用对讲机一样。也就是说,好好的全双工 TCP,被 HTTP/1.1 用成了半双工。
也就是说,好好的全双工 TCP,被 HTTP/1.1 用成了半双工。
Web Socket 是 HTML5 标准的一部分
- 主要 Web 浏览器的最新版本支持
- JavaScript 中的简单 API
- Libraries also available on iOS and Android
浏览器如何建立 WebSocket 连接
浏览器在 TCP 三次握手建立连接之后,都统一使用 HTTP 协议先进行一次通信。
- 如果此时是普通的 HTTP 请求,那后续双方就还是老样子继续用普通 HTTP 协议进行交互,
- 如果这时候是想建立WebSocket连接,就会在HTTP请求里带上一些特殊的header头
WebSocket的消息格式
上面提到在完成协议升级之后,两端就会用webscoket的数据格式进行通信。数据包在WebSocket中被叫做帧:
payload字段:存放的是我们真正想要传输的数据的长度,单位是字节。
REF
- https://developer.aliyun.com/article/1487833
- https://blog.csdn.net/zhangbijun1230/article/details/89083557
- https://worktile.com/kb/ask/1951724.html
- https://www.xiaoweigod.com/network/1553.html
- https://blog.csdn.net/phoenix1415/article/details/122834480
- https://blog.zilch40.wang/post/way-to-access-a-local-server-from-internet/
- https://blog.csdn.net/weixin_43925630/article/details/139178999
- https://xiaolincoding.com/network/2_http/http_websocket.html#长轮询