【稿】
陈硕《网络编程实战》
02 一个TCP的简单实验
站在巨人的肩膀之上。
按照录像整理,部分专有名词不太肯定,版权归陈硕大神。
实验用的是我家里的几台计算机,第一台的主机名叫atom,现在是一台英特尔赛扬1037的机器,是一个低端的Ivy Bridge,双核1.8GHZ,8G内存,它叫atom,因为它原来是一台英特尔的,atom D525,后来那个主板出了点问题,换成了赛扬 Celeron 1037U。
第二台是e6400,它是2009年年底,我买的一台戴尔笔记本,型号就是e6400,它是双核的英特尔,酷睿2.4GHz 处理器,4G内存。
第三台机器比较低端,更低端,是AMD的E-350,是一个低端的APU,8G内存。
这门课程,没有在服务器的高端硬件上测试性能,因为性能数据很容易过时,如果我测的性能,让你留下了错误的印象,那就不如没有,所以我干脆就用特别慢的机器测,一个关键是掌握测性能的办法,不要把这个性能数据记下来,应用到以后的这个编程实践中,这个行为是属于买椟还珠。
我们现在做这个实验,是验证一下TCP的千兆网有效带宽。
我们先做一个简单的测试,就是用 netcat 和 dd 测收发数据,测试方法是从 atom 发送到 e6400,e6400 在 5001 端口监听数据,然后把数据收下来,转到 /dev/null,转到一个黑洞里面,然后 atom 这个机器是从 /dev/zero 读一千兆字节的数据,然后发送到e6400的5001端口,这个大概运行应该是十秒钟,dd会打印出带宽,所以我们不用再测别的,这个打印出来,它刚好拷了1.0GB的数据,刚好用了8.49秒,带宽是118兆字节/秒。
因为dd把那个数据四舍五入了,我们前面算出来是117.68,这里四舍五入等于118,说明这个理论计算和实践还是比较接近的。
那自然,我们还会想到,如果是本机测试,这个性能会怎样的,你就让atom发给atom,这个估计会快一些,所以我把这个,数据量从1 个 G 改成 10 个 G,然后发到本机,这个大概要执行二十秒的样子,然后我们可以想办法,它现在只有580兆字节/秒,这个数据其实比较低。
我们可以再来试验一下别的情况,上面我们做的是dd从/dev/zero 读数据,如果我们从磁盘上读看,是会更快还是更慢呢?不一定,我在这台机器的这个用户目录下,有一个1GB左右的文件,这个文件是1.1GB,是FreeBSD的安装文件,如果我们把它,发送到本机通过nc的话看看有多快,这个弄的是IO重定向的办法,我们这里要提前用一个 time,因为现在没有直接测试时间的工具,没有用 dd 了。
这个第一次运行,我看看有多长时间,第一次运行用了九点四秒,你看一下这个文件的具体大小是1087774720 bit,这么多,然后我们可以算出来,他的那个带宽是多少,1087774720/9.0 =>115720714,再除以1,000,000,=115.72,这个数据不是网络性,是磁盘性的,因为我们可以再试一遍,再运行一下这个,这次只用了1.012秒,我们可以再算一遍,1.012秒,这个数据就是啊,第二次测的数据是1074.87兆字节每秒。
这里我们把这个结果再回顾一下,
第一次我们测出来,用这个dd是580兆字节每秒,
第二次,我们从磁盘读这个文件,测出来是115.72兆字节每秒,
第三次用相同的命令行也是从磁盘读,不过这次数据已经缓存到内存里面了,他只用了 1074 字节每秒,
所以测试方法很重要,你这现在想一想,为什么,这个都是从内存里的,没有和磁盘打交道的,一个是1074,一个是dd测出来,只有580。原因是因为dd本身也占了一些资源。
我们可以再做一个进一步实验,那就是还是用dd,但是这次我们在接收端的时候给他加一个,显示这个带宽的数据接收速度的工具叫pv,那这个我们先考虑一下他应该会更慢一点,440、450多兆字节每秒,那个应该要运行二十来秒的样子,是477,下面测出来是455。因为这是一台双核的机器,我们实际上起了四个进程,dd一个,nc一个,下面一个接收nc的一个,pv一个,四个进程去分两个核的话会出现一定的争用情况。
而原来的情况,在最快的测出来那次是一千一百,啊,1074的时候,我们实际上只起了两个进程,一个是nc,一个是接收的nc,这个或许就能解释,为什么我们这个性能有这么大差别,你如果自己做实验的话,也可以把top打开,先看一下哪个进程占的CPU的情况,我现在这个屏幕不够大,所以就这一步就免了,我们再来一个测试,就是把数据发送到一台更慢的机器,e350上,那看看这个,e350到底有多慢,这样看不到这个实时的进度,可能要等一小会儿,e350还是能做到118兆字节每秒,说明这个,TCP协议栈的开销确实是不大,因为即便是e350,这个低端的机器都能够把一个千兆网的带宽给用完。
当然了我们如果加上一些别的操作,那这个性能可能会下降,我们可以不妨再试一下,这里加上那个pv,这样我们就能看一些比较实时的进度,一般时注意PV用的那个单位,他用的是二进制的兆字节,他不是十进制所以两个数据不一样,一个是118,一个是112,其实是单位不同,这个地方应该用MIB准确的说,总结一下我们刚才测试的性能。
结果,在两台机器之间,千兆网TCP的用,netcat测出来,吞吐量是118兆字节每秒,在atom 这台机器上,如果用dd,然后重定向的nc,然后另外一套在同样的机器上用,nc测的数据是580兆字节每秒,在atom机器上,同样一台机器不用dd而改,从文件重定向测,服务端不变的话是1074兆字节每秒,这两个数字,即便是在atom就是英特尔赛扬的机器上也是太低了。
下面我解释一下其中的原因,这个数字是这样,第一种测试的时候,我们先画一个,下面这个是内核态,看到上面是用户态,就是,dd 是一个进程,这个进程,它是从文件,是那个/dev/zero读,这样读到dd,然后写到一个管道里面 pipe,然后nc这是另外一个进程,从这个管道里面读出来,然后再发到这个TCP,本机的TCP。
所以,你看他实际上有四次数据从用户到内核的拷贝,这还只是客户端。
服务端的话,同样有两次,一个是从TCP读出来,这样读回来,这是服务端的nc,然后他它再重定向到那个文件上去,所以这个写到内核 /dev/null,所以我们测出来580兆不是光有TCP的这么一段,而是包括了另外两个。
另外,乘以二的开销,所以一共是三倍,所以我们看到这个580兆,那可以想象如果只有TCP的话应该是这个的三倍,应该是1.6G字节每秒左右。
第二种情况不一样,是因为他少了前面这个dd这一步,他是直接从文件读,所以,是从这开始让换一条分隔线,nc是从文件,而且已经是缓存在内存里的文件读进来,然后再写到TCP,然后服务端不变,是从TCP读进来,然后写到/dev/null,所以它性能会提高很多。
另外还要再说明两点:
一个是我们用的这个nc是,这个nc命令,OpenBSD的版本,在Linux还有一个叫做传统的nc,这两个命令行参数有所不同;
第二个是我们在第三个例子里面会自己实现一个netcat,那个自己实现的功能比较简单,而且性能会比这个高很多。这个到了第三讲,我们再来看,这里先提一点,这就是nc这个工具简单测一下TCP吞吐量的结果。