AImager

网络『流』

#粘包

学计算机网络的时候,大部分书籍都是以分层模型来讲解网络的结构的,时间长了,部分同学就形成了网络package的中心概念。但另一方面,有经验的程序员又不断强调网络本质是『流』,那么两个概念孰是孰非?我们一点一点来说。

带宽与传输速率

带宽这个概念来源于信息论,指的是传输频率,即有效通过信道的最大信号频带宽度,以赫兹为单位。而传输速率指单位时间通过信道的比特量,最小单位为b/s。所以这两个概念在信息论里是不同的,但也存在关联,香农定理就是描述两者关联的重要定理。

信息论是严谨的学科,所以需要对概念进行严格的区分。而到了现实应用上,这两个概念就开始趋同了,加上ISP的『普及』,大家基本上认同了带宽就是传输速率。当然,这没有什么大问题,而且对于普通大众而言,传输速率比传输频率好理解的多,至于为什么说成带宽,可能是带宽这个名词更符合直觉吧!

结合我们开始说的问题,你会发现带宽概念的引入基本上说明了网络就是流,因为从网络的最底层看,它就是电流,至于所说的网络带宽也就反应了单位时间内电流变化的情况。

粘包

网络的本质是『流』,但对于网络开发者而言,最常用的却是send和recv这样的类package使用方法。其实这本没有什么问题,但如果对底层了解不够,最终的结果就和预期的有差别,其中比较经典的一类问题被大家抽象出来了,即『粘包』。

『粘包』是个比较有争议的概念,但『粘包』的现象是切实存在的,它主要指send和recv不按对应关系出现的现象,不好理解?举个例子

/* client */
send("1234");
send("5678");

/* server */
while(1) {
    res = recv();
    printf("%s_",res);
}

上面是一段client和server的伪码,如果『粘包』不存在,那么理想情况下只会有1234_5678_的结果,但是实际上可能会出现

  1. 1234_5678_
  2. 123456_78_
  3. 5678_1234_

1是很符合直觉的情况,send和recv一一对应,不过出现这种情况的限制反而严格些。比如recv用户空间的buff刚好4字节;或者每次内核buff收到一个send的时候,刚好应用就进行recv获取数据。

2即『包截断』,一个可能情景是recv的时候内核buff区已经存在12345678,而用户buff区的大小为6B,因此分两次才取出数据打印;

3即『包乱序』,这种情况在TCP下不可能发生,在UDP下则有可能,因为UDP没有滑动窗口保证有序,则5678在内核buff区可能排在1234的前面,从而导致recv的时候先取出5678。注意,这里说5678可能排在前面,而不是说5678先到,是因为无论UDP还是TCP都可能5678先到,但只有TCP会通过流量窗口保证有序。

有部分开发者认为这都是开发者把网路理解为package的结果,确实,如果按照前面的结论,那网络为package的结论显然是错的,但真实的网络开发场景下,package的理解反而更正确,有以下两点证明

  1. 最终的发送总是以一定长度的package为单位的,而且对网络而言,这段长度的数据就是传输的最小单位,它们只可能同时丢包或者在同一网络线路上传输,而不可能出现部分可用的情况,因为即使部分到了,也会校验不通过被丢弃。
  2. 网络模型总是以package分层的模式来实现。

当然,把网络理解成package或流并不是出问题的直接因素,更多情况可能是对实现细节的不了解,所以如果不是为了交流,概念的准确性事实上是没有细节理解来的重要的,毕竟,保证结果的是后者。

参考链接