当前位置: 首页 > news >正文

网络编程的一些胡思乱想

以下胡乱列出我对网络编程的一些想法,前后无关联。

网络编程是什么?

网络编程是什么?是熟练使用Sockets API吗?说实话,在实际项目里我只用过两次Sockets API,其他时候都是使用封装好的网络库。

第一次是2005年在学校做一个羽毛球赛场计分系统:我用C# 编写运行在PC机上的软件,负责比分的显示;再用C# 写了运行在PDA上的计分界面,记分员拿着PDA记录比分;这两部分程序通过 TCP协议相互通信。这其实是个简单的分布式系统,体育馆有不止一片场地,每个场地都有一名拿PDA的记分员,每个场地都有两台显示比分的PC机(显示器是42吋平板电视,放在场地的对角,这样两边看台的观众都能看到比分)。这两台PC机功能不完全一样,一台只负责显示当前比分,另一台还要负责与PDA通信,并更新数据库里的比分信息。此外,还有一台PC机负责周期性地从数据库读出全部7片场地的比分,显示在体育馆墙上的大屏幕上。这台PC上还运行着一个程序,负责生成比分数据的静态页面,通过FTP上传发布到某门户网站的体育频道。系统中还有一个录入赛程(参赛队,运动员,出场顺序等)数据库的程序,运行在数据库服务器上。算下来整个系统有十来个程序,运行在二十多台设备(PC和PDA)上,还要考虑可靠性。将来有机会把这个小系统仔细讲一讲,挺有意思的。

这是我第一次写实际项目中的网络程序,当时写下来的感觉是像写命令行与用户交互的程序:程序在命令行输出一句提示语,等待客户输入一句话,然后处理客户输入,再输出下一句提示语,如此循环。只不过这里的“客户”不是人,而是另一个程序。在建立好TCP连接之后,双方的程序都是read/write循环(为求简单,我用的是blocking读写),直到有一方断开连接。

第二次是2010年编写muduo网络库,我再次拿起了Sockets API,写了一个基于Reactor模式的C++ 网络库。写这个库的目的之一就是想让日常的网络编程从Sockets API的琐碎细节中解脱出来,让程序员专注于业务逻辑,把时间用在刀刃上。Muduo 网络库的示例代码包含了几十个网络程序,这些示例程序都没有直接使用Sockets API。

在此之外,无论是实习还是工作,虽然我写的程序都会通过TCP协议与其他程序打交道,但我没有直接使用过Sockets API。对于TCP网络编程,我认为核心是处理“三个半事件”,见《Muduo 网络编程示例之零:前言》中的“TCP 网络编程本质论”。程序员的主要工作是在事件处理函数中实现业务逻辑,而不是和Sockets API较劲。

这里还是没有说清楚“网络编程”是什么,请继续阅读后文“网络编程的各种任务角色”。

学习网络编程有用吗?

以上说的是比较底层的网络编程,程序代码直接面对从TCP或UDP收到的数据以及构造数据包发出去。在实际工作中,另一种常见 的情况是通过各种 client library 来与服务端打交道,或者在现成的框架中填空来实现server,或者采用更上层的通信方式。比如用libmemcached与memcached打交道,使用libpq来与PostgreSQL 打交道,编写Servlet来响应http请求,使用某种RPC与其他进程通信,等等。这些情况都会发生网络通信,但不一定算作“网络编程”。如果你的工作是前面列举的这些,学习TCP/IP网络编程还有用吗?

我认为还是有必要学一学,至少在troubleshooting 的时候有用。无论如何,这些library或framework都会调用底层的Sockets API来实现网络功能。当你的程序遇到一个线上问题,如果你熟悉Sockets API,那么从strace不难发现程序卡在哪里,尽管可能你没有直接调用这些Sockets API。另外,熟悉TCP/IP协议、会用tcpdump也大大有助于分析解决线上网络服务问题。

在什么平台上学习网络编程?

对于服务端网络编程,我建议在Linux上学习。

如果在10年前,这个问题的答案或许是FreeBSD,因为FreeBSD根正苗红,在2000年那一次互联网浪潮中扮演了重要角色,是很多公司首选的免费服务器操作系统。2000年那会儿Linux还远未成熟,连epoll都还没有实现。(FreeBSD在2001年发布4.1版,加入了kqueue,从此C10k不是问题。)

10年后的今天,事情起了变化,Linux成为了市场份额最大的服务器操作系统(http://en.wikipedia.org/wiki/Usage_share_of_operating_systems)。在Linux这种大众系统上学网络编程,遇到什么问题会比较容易解决。因为用的人多,你遇到的问题别人多半也遇到过;同样因为用的人多,如果真的有什么内核bug,很快就会得到修复,至少有work around的办法。如果用别的系统,可能一个问题发到论坛上半个月都不会有人理。从内核源码的风格看,FreeBSD更干净整洁,注释到位,但是无奈它的市场份额远不如Linux,学习Linux是更好的技术投资。

可移植性重要吗?

写网络程序要不要考虑移植性?这取决于项目需要,如果贵公司做的程序要卖给其他公司,而对方可能使用Windows、Linux、FreeBSD、Solaris、AIX、HP-UX等等操作系统,这时候考虑移植性。如果编写公司内部的服务器上用的网络程序,那么大可只关注一个平台,比如Linux。因为编写和维护可移植的网络程序的代价相当高,平台间的差异可能远比想象中大,即便是POSIX系统之间也有不小的差异(比如Linux没有SO_NOSIGPIPE选项),错误的返回码也大不一样。

我就不打算把muduo往Windows或其他操作系统移植。如果需要编写可移植的网络程序,我宁愿用libevent或者Java Netty这样现成的库,把脏活累活留给别人。

网络编程的各种任务角色

计算机网络是个 big topic,涉及很多人物和角色,既有开发人员,也有运维人员。比方说:公司内部两台机器之间 ping 不通,通常由网络运维人员解决,看看是布线有问题还是路由器设置不对;两台机器能ping通,但是程序连不上,经检查是本机防火墙设置有问题,通常由系统管理员解决;两台机器能连上,但是丢包很严重,发现是网卡或者交换机的网口故障,由硬件维修人员解决;两台机器的程序能连上,但是偶尔发过去的请求得不到响应,通常是程序bug,应该由开发人员解决。

本文主要关心开发人员这一角色。下面简单列出一些我能想到的跟网络打交道的编程任务,其中前三项是面向网络本身,后面几项是在计算机网络之上构建信息系统。

1. 开发网络设备,编写防火墙、交换机、路由器的固件 firmware

2. 开发或移植网卡的驱动

3. 移植或维护TCP/IP协议栈(特别是在嵌入式系统上)

4. 开发或维护标准的网络协议程序,HTTP、FTP、DNS、SMTP、POP3、NFS

5. 开发标准网络协议的“附加品”,比如HAProxy、squid、varnish等web load balancer

6. 开发标准或非标准网络服务的客户端库,比如ZooKeeper客户端库,memcached客户端库

7. 开发与公司业务直接相关的网络服务程序,比如即时聊天软件的后台服务器,网游服务器,金融交易系统,互联网企业用的分布式海量存储,微博发帖的内部广播通知,等等

8. 客户端程序中涉及网络的部分,比如邮件客户端中与 POP3、SMTP通信的部分,以及网游的客户端程序中与服务器通信的部分

本文所指的“网络编程”专指第7项,即在TCP/IP协议之上开发业务软件。

面向业务的网络编程的特点

跟开发通用的网络程序不同,开发面向公司业务的专用网络程序有其特点:

· 业务逻辑比较复杂,而且时常变化

如果写一个HTTP服务器,在大致实现HTTP /1.1标准之后,程序的主体功能一般不会有太大的变化,程序员会把时间放在性能调优和bug修复上。而开发针对公司业务的专用程序时,功能说明书(spec)很可能不如HTTP/1.1标准那么细致明确。更重要的是,程序是快速演化的。以即时聊天工具的后台服务器为例,可能第一版只支持在线聊天;几个月之后发布第二版,支持离线消息;又过了几个月,第三版支持隐身聊天;随后,第四版支持上传头像;如此等等。这要求程序员能快速响应新的业务需求,公司才能保持竞争力。

· 不一定需要遵循公认的通信协议标准

比方说网游服务器就没什么协议标准,反正客户端和服务端都是本公司开发,如果发现目前的协议设计有问题,两边一起改了就是了。

· 程序结构没有定论

对于高并发大吞吐的标准网络服务,一般采用单线程事件驱动的方式开发,比如HAProxy、lighttpd等都是这个模式。但是对于专用的业务系统,其业务逻辑比较复杂,占用较多的CPU资源,这种单线程事件驱动方式不见得能发挥现在多核处理器的优势。这留给程序员比较大的自由发挥空间,做好了横扫千军,做烂了一败涂地。

· 性能评判的标准不同

如果开发httpd这样的通用服务,必然会和开源的Nginx、lighttpd等高性能服务器比较,程序员要投入相当的精力去优化程序,才能在市场上占有一席之地。而面向业务的专用网络程序不一定有开源的实现以供对比性能,程序员通常更加注重功能的稳定性与开发的便捷性。性能只要一代比一代强即可。

· 网络编程起到支撑作用,但不处于主导地位

程序员的主要工作是实现业务逻辑,而不只是实现网络通信协议。这要求程序员深入理解业务。程序的性能瓶颈不一定在网络上,瓶颈有可能是CPU、Disk IO、数据库等等,这时优化网络方面的代码并不能提高整体性能。只有对所在的领域有深入的了解,明白各种因素的权衡(trade-off),才能做出一些有针对性的优化。

几个术语

互联网上的很多口水战是由对同一术语的不同理解引起的,比我写的《多线程服务器的适用场合》就曾经人被说是“挂羊头卖狗肉”,因为这篇文章中举的 master例子“根本就算不上是个网络服务器。因为它的瓶颈根本就跟网络无关。”

· 网络服务器

“网络服务器”这个术语确实含义模糊,到底指硬件还是软件?到底是服务于网络本身的机器(交换机、路由器、防火墙、NAT),还是利用网络为其他人或程序提供服务的机器(打印服务器、文件服务器、邮件服务器)。每个人根据自己熟悉的领域,可能会有不同的解读。比方说或许有人认为只有支持高并发高吞吐的才算是网络服务器。

为了避免无谓的争执,我只用“网络服务程序”或者“网络应用程序”这种含义明确的术语。“开发网络服务程序”通常不会造成误解。

· 客户端?服务端?

在TCP网络编程里边,客户端和服务端很容易区分,主动发起连接的是客户端,被动接受连接的是服务端。当然,这个“客户端”本身也可能是个后台服务程序,HTTP Proxy对HTTP Server来说就是个客户端。

· 客户端编程?服务端编程?

但是“服务端编程”和“客户端编程”就不那么好区分。比如 Web crawler,它会主动发起大量连接,扮演的是HTTP客户端的角色,但似乎应该归入“服务端编程”。又比如写一个 HTTP proxy,它既会扮演服务端——被动接受 web browser 发起的连接,也会扮演客户端——主动向 HTTP server 发起连接,它究竟算服务端还是客户端?我猜大多数人会把它归入服务端编程。

那么究竟如何定义“服务端编程”?

服务端编程需要处理大量并发连接?也许是,也许不是。比如云风在一篇介绍网游服务器的博客http://blog.codingnow.com/2006/04/iocp_kqueue_epoll.html中就谈到,网游中用到的“连接服务器”需要处理大量连接,而“逻辑服务器”只有一个外部连接。那么开发这种网游“逻辑服务器”算服务端编程还是客户端编程呢?

我认为,“服务端网络编程”指的是编写没有用户界面的长期运行的网络程序,程序默默地运行在一台服务器上,通过网络与其他程序打交道,而不必和人打交道。与之对应的是客户端网络程序,要么是短时间运行,比如wget;要么是有用户界面(无论是字符界面还是图形界面)。本文主要谈服务端网络编程。

7x24重要吗?内存碎片可怕吗?

一谈到服务端网络编程,有人立刻会提出7x24运行的要求。对于某些网络设备而言,这是合理的需求,比如交换机、路由器。对于开发商业系统,我认为要求程序7x24运行通常是系统设计上考虑不周。具体见《分布式系统的工程化开发方法》第20页起。重要的不是7x24,而是在程序不必做到7x24的情况下也能达到足够高的可用性。一个考虑周到的系统应该允许每个进程都能随时重启,这样才能在廉价的服务器硬件上做到高可用性。

既然不要求7x24,那么也不必害怕内存碎片,理由如下:

· 64-bit系统的地址空间足够大,不会出现没有足够的连续空间这种情况。

· 现在的内存分配器(malloc及其第三方实现)今非昔比,除了memcached这种纯以内存为卖点的程序需要自己设计分配器之外,其他网络程序大可使用系统自带的malloc或者某个第三方实现。

· Linux Kernel也大量用到了动态内存分配。既然操作系统内核都不怕动态分配内存造成碎片,应用程序为什么要害怕?

· 内存碎片如何度量?有没有什么工具能为当前进程的内存碎片状况评个分?如果不能比较两种方案的内存碎片程度,谈何优化?

有人为了避免内存碎片,不使用STL容器,也不敢new/delete,这算是premature optimization还是因噎废食呢?

协议设计是网络编程的核心

对于专用的业务系统,协议设计是核心任务,决定了系统的开发难度与可靠性,但是这个领域还没有形成大家公认的设计流程。

系统中哪个程序发起连接,哪个程序接受连接?如果写标准的网络服务,那么这不是问题,按RFC来就行了。自己设计业务系统,有没有章法可循?以网游为例,到底是连接服务器主动连接逻辑服务器,还是逻辑服务器主动连接“连接服务器”?似乎没有定论,两种做法都行。一般可以按照“依赖->被依赖”的关系来设计发起连接的方向。

比新建连接难的是关闭连接。在传统的网络服务中(特别是短连接服务),不少是服务端主动关闭连接,比如daytime、HTTP/1.0。也有少部分是客户端主动关闭连接,通常是些长连接服务,比如 echo、chargen等。我们自己的业务系统该如何设计连接关闭协议呢?

服务端主动关闭连接的缺点之一是会多占用服务器资源。服务端主动关闭连接之后会进入TIME_WAIT状态,在一段时间之内hold住一些内核资源。如果并发访问量很高,这会影响服务端的处理能力。这似乎暗示我们应该把协议设计为客户端主动关闭,让TIME_WAIT状态分散到多台客户机器上,化整为零。

这又有另外的问题:客户端赖着不走怎么办?会不会造成拒绝服务攻击?或许有一个二者结合的方案:客户端在收到响应之后就应该主动关闭,这样把 TIME_WAIT 留在客户端。服务端有一个定时器,如果客户端若干秒钟之内没有主动断开,就踢掉它。这样善意的客户端会把TIME_WAIT留给自己,buggy的客户端会把 TIME_WAIT留给服务端。或者干脆使用长连接协议,这样避免频繁创建销毁连接。

比连接的建立与断开更重要的是设计消息协议。消息格式很好办,XML、JSON、Protobuf都是很好的选择;难的是消息内容。一个消息应该包含哪些内容?多个程序相互通信如何避免race condition(见《分布式系统的工程化开发方法》p.16的例子)?系统的全局状态该如何跃迁?可惜这方面可供参考的例子不多,也没有太多通用的指导原则,我知道的只有30年前提出的end-to-end principle和happens-before relationship。只能从实践中慢慢积累了。

网络编程的三个层次

侯捷先生在《漫談程序員與編程》中讲到 STL 运用的三个档次:“會用STL,是一種檔次。對STL原理有所了解,又是一個檔次。追蹤過STL源碼,又是一個檔次。第三種檔次的人用起 STL 來,虎虎生風之勢絕非第一檔次的人能夠望其項背。”

我认为网络编程也可以分为三个层次:

1. 读过教程和文档

2. 熟悉本系统TCP/IP协议栈的脾气

3. 自己写过一个简单的TCP/IP stack

第一个层次是基本要求,读过《Unix网络编程》这样的编程教材,读过《TCP/IP详解》基本理解TCP/IP协议,读过本系统的manpage。这个层次可以编写一些基本的网络程序,完成常见的任务。但网络编程不是照猫画虎这么简单,若是按照manpage的功能描述就能编写产品级的网络程序,那人生就太幸福了。

第二个层次,熟悉本系统的TCP/IP协议栈参数设置与优化是开发高性能网络程序的必备条件。摸透协议栈的脾气还能解决工作中遇到的比较复杂的网络问题。拿Linux的TCP/IP协议栈来说:

· 有可能出现自连接(见《学之者生,用之者死——ACE历史与简评》举的三个硬伤),程序应该有所准备。

· Linux的内核会有bug,比如某种TCP拥塞控制算法曾经出现TCP window clamping(窗口箝位)bug,导致吞吐量暴跌,可以选用其他拥塞控制算法来绕开(work around)这个问题。

这些阴暗角落在manpage里没有描述,要通过其他渠道了解。

编写可靠的网络程序的关键是熟悉各种场景下的error code(文件描述符用完了如何?本地ephemeral port暂时用完,不能发起新连接怎么办?服务端新建并发连接太快,backlog用完了,客户端connect会返回什么错误?),有的在manpage里有描述,有的要通过实践或阅读源码获得。

第三个层次,通过自己写一个简单的TCP/IP协议栈,能大大加深对TCP/IP的理解,更能明白TCP为什么要这么设计,有哪些因素制约,每一步操作的代价是什么,写起网络程序来更是成竹在胸。

其实实现TCP/IP只需要操作系统提供三个接口函数:一个函数,两个回调函数。分别是:send_packet()、on_receive_packet()、on_timer()。多年前有一篇文章《使用libnet与libpcap构造TCP/IP协议软件》介绍了在用户态实现TCP/IP的方法。lwIP也是很好的借鉴对象。

如果有时间,我打算自己写一个Mini/Tiny/Toy/Trivial/Yet-Another TCP/IP。我准备换一个思路,用TUN/TAP设备在用户态实现一个能与本机点对点通信的TCP/IP协议栈,这样那三个接口函数就表现为我最熟悉的文件读写。在用户态实现的好处是便于调试,协议栈做成静态库,与应用程序链接到一起(库的接口不必是标准的Sockets API)。做完这一版,还可以继续发挥,用FTDI的USB-SPI接口芯片连接ENC28J60适配器,做一个真正独立于操作系统的TCP/IP stack。如果只实现最基本的IP、ICMP Echo、TCP的话,代码应能控制在3000行以内;也可以实现UDP,如果应用程序需要用到DNS的话。

最主要的三个例子

我认为TCP网络编程有三个例子最值得学习研究,分别是echo、chat、proxy,都是长连接协议。

Echo的作用:熟悉服务端被动接受新连接、收发数据、被动处理连接断开。每个连接是独立服务的,连接之间没有关联。在消息内容方面Echo有一些变种:比如做成一问一答的方式,收到的请求和发送响应的内容不一样,这时候要考虑打包与拆包格式的设计,进一步还可以写简单的HTTP服务。

Chat的作用:连接之间的数据有交流,从a收到的数据要发给b。这样对连接管理提出的更高的要求:如何用一个程序同时处理多个连接?fork() per connection似乎是不行的。如何防止串话?b有可能随时断开连接,而新建立的连接c可能恰好复用了b的文件描述符,那么a会不会错误地把消息发给c?

Proxy的作用:连接的管理更加复杂:既要被动接受连接,也要主动发起连接,既要主动关闭连接,也要被动关闭连接。还要考虑两边速度不匹配,见《Muduo 网络编程示例之十:socks4a 代理服务器》。

这三个例子功能简单,突出了TCP网络编程中的重点问题,挨着做一遍基本就能达到层次一的要求。

TCP的可靠性有多高?

TCP是“面向连接的、可靠的、字节流传输协议”,这里的“可靠”究竟是什么意思?《Effective TCP/IP Programming》第9条说:Realize That TCP Is a Reliable Protocol, Not an Infallible Protocol,那么TCP在哪种情况下会出错?这里说的“出错”指的是收到的数据与发送的数据不一致,而不是数据不可达。

我在《一种自动反射消息类型的 Google Protobuf 网络传输方案》中设计了带check sum的消息格式,很多人表示不理解,认为是多余的。IP header里边有check sum,TCP header也有check sum,链路层以太网还有CRC32校验,那么为什么还需要在应用层做校验?什么情况下TCP传送的数据会出错?

IP header和TCP header的check sum是一种非常弱的16-bit check sum算法,把数据当成反码表示的16-bit integers,再加到一起。这种checksum算法能检出一些简单的错误,而对某些错误无能为力,由于是简单的加法,遇到“和”不变的情况就无法检查出错误(比如交换两个16-bit整数,加法满足交换律,结果不变)。以太网的CRC32只能保证同一个网段上的通信不会出错(两台机器的网线插到同一个交换机上,这时候以太网的CRC是有用的)。但是,如果两台机器之间经过了多级路由器呢?

上图中Client向Server发了一个TCP segment,这个segment先被封装成一个IP packet,再被封装成ethernet frame,发送到路由器(图中消息a)。Router收到ethernet frame (b),转发到另一个网段(c),最后Server收到d,通知应用程序。Ethernet CRC能保证a和b相同,c和d相同;TCP header check sum的强度不足以保证收发payload的内容一样。另外,如果把Router换成NAT,那么NAT自己会构造c(替换掉源地址),这时候a和d的payload不能用tcp header checksum校验。

路由器可能出现硬件故障,比方说它的内存故障(或偶然错误)导致收发IP报文出现多bit的反转或双字节交换,这个反转如果发生在payload区,那么无法用链路层、网络层、传输层的check sum查出来,只能通过应用层的check sum来检测。这个现象在开发的时候不会遇到,因为开发用的几台机器很可能都连到同一个交换机,ethernet CRC能防止错误。开发和测试的时候数据量不大,错误很难发生。之后大规模部署到生产环境,网络环境复杂,这时候出个错就让人措手不及。有一篇论文《When the CRC and TCP checksum disagree》分析了这个问题。另外《The Limitations of the Ethernet CRC and TCP/IP checksums for error detection》( http://noahdavids.org/self_published/CRC_and_checksum.html )也值得一读。

这个情况真的会发生吗?会的,Amazon S3 在2008年7月就遇到过,单bit反转导致了一次严重线上事故,所以他们吸取教训加了 check sum。见http://status.aws.amazon.com/s3-20080720.html

另外一个例证:下载大文件的时候一般都会附上MD5,这除了有安全方面的考虑(防止篡改),也说明应用层应该自己设法校验数据的正确性。这是end-to-end principle的一个例证。

三本必看的书

谈到Unix编程和网络编程,W. Richard Stevens 是个绕不开的人物,他生前写了6本书,APUE、两卷UNP、三卷TCP/IP。有四本与网络编程直接相关。UNP第二卷其实跟网络编程关系不大,是APUE在多线程和进程间通信(IPC)方面的补充。很多人把TCP/IP一二三卷作为整体推荐,其实这三本书用处不同,应该区别对待。

这里谈到的几本书都没有超出孟岩在《TCP/IP 网络编程之四书五经》中的推荐,说明网络编程这一领域已经相对成熟稳定。

· 《TCP/IP Illustrated,Vol. 1: The Protocols》中文名《TCP/IP 详解》,以下简称 TCPv1。

TCPv1 是一本奇书。

这本书迄今至少被三百多篇学术论文引用过http://portal.acm.org/citation.cfm?id=161724。一本学术专著被论文引用算不上出奇,难得的是一本写给程序员看的技术书能被学术论文引用几百次,我不知道还有哪本技术书能做到这一点。

TCPv1 堪称 TCP/IP领域的圣经。作者 W. Richard Stevens 不是 TCP/IP 协议的发明人,他从使用者(程序员)的角度,以 tcpdump 为工具,对 TCP 协议抽丝剥茧娓娓道来(第17~24章),让人叹服。恐怕 TCP 协议的设计者也难以讲解得如此出色,至少不会像他这么耐心细致地画几百幅收发 package 的时序图。

TCP作为一个可靠的传输层协议,其核心有三点:

1. Positive acknowledgement with retransmission

2. Flow control using sliding window(包括Nagle 算法等)

3. Congestion control(包括slow start、congestion avoidance、fast retransmit等)

第一点已经足以满足“可靠性”要求(为什么?);第二点是为了提高吞吐量,充分利用链路层带宽;第三点是防止过载造成丢包。换言之,第二点是避免发得太慢,第三点是避免发得太快,二者相互制约。从反馈控制的角度看,TCP像是一个自适应的节流阀,根据管道的拥堵情况自动调整阀门的流量。

TCP的 flow control 有一个问题,每个TCP connection是彼此独立的,保存有自己的状态变量;一个程序如果同时开启多个连接,或者操作系统中运行多个网络程序,这些连接似乎不知道他人的存在,缺少对网卡带宽的统筹安排。(或许现代的操作系统已经解决了这个问题?)

TCPv1 唯一的不足是它出版太早了,1993 年至今网络技术发展了几代。链路层方面,当年主流的 10Mbit 网卡和集线器早已经被淘汰;100Mbit 以太网也没什么企业在用了,交换机(switch)也已经全面取代了集线器(hub);服务器机房以 1Gbit 网络为主,有些场合甚至用上了 10Gbit 以太网。另外,无线网的普及也让TCP flow control面临新挑战;原来设计TCP的时候,人们认为丢包通常是拥塞造成的,这时应该放慢发送速度,减轻拥塞;而在无线网中,丢包可能是信号太弱造成的,这时反而应该快速重试,以保证性能。网络层方面变化不大,IPv6 雷声大雨点小。传输层方面,由于链路层带宽大增,TCP window scale option 被普遍使用,另外 TCP timestamps option 和 TCP selective ack option 也很常用。由于这些因素,在现在的 Linux 机器上运行 tcpdump 观察 TCP 协议,程序输出会与原书有些不同。

http://www.jsqmd.com/news/1125864/

相关文章:

  • UTBotJava多语言支持指南:Java、Kotlin、Python、Go、JavaScript全覆盖
  • 开源CLI工具安全调用国产大模型API实战
  • 鹤壁办宴席,选烟酒怎么备不浪费又体面?
  • 企业网络管理实战:稳定、安全、高效运维全方案
  • Unity基础:Game视图详解——游戏预览、分辨率模拟与性能显示
  • sklearn 生成数据集 make_classification 参数详解:创建3类不平衡分类数据实战
  • 为什么网卡停止收包?——Intel网卡RX Buffer Replenishment机制深度解析(下)
  • 2026年洛阳新房装修:水管漏水半夜打电话,洛阳这家装修公司居然秒回!
  • 一体化泵站哪家技术强
  • 为什么要让我们的“领域模型”裸奔?(上)
  • 罗氏线圈柔性电流探头在测试中的应用
  • 搜维尔科技:TESOLLO灵巧手与Mnaus数据手套遥操作方案
  • OEXN:“特斯拉加码车型刺激需求”
  • PW7126+PW4406A*4三串锂电池充放电保护板方案,持续6A,过流保护7A
  • Affinity Matrix 构建实战:3种相似度度量(Cosine/Jaccard)对比与 Scikit-learn 实现
  • Python 自动化之批量图片处理——水印、压缩、格式转换
  • gmail loading progress bar 实现原理
  • 基于微软Dryad分布式并行计算平台云技术的研究
  • MIX 11 细节梳理 Windows phone 7 Session
  • Codex代理配置实战:用国产大模型替代OpenAI API的完整指南
  • 绝影马:7.8起美国CPSC电子申报强制执行,未合规将遭清关扣留!
  • ParsecVDisplay:Windows虚拟显示器的终极免费解决方案
  • 从团队项目角度看 AI API 聚合平台:别等成本失控后才补日志
  • 2026深度研习八字排盘工具怎么选:看结构复盘、案例沉淀和AI边界
  • 首先在code behind中加入以下方法
  • 一撸猫就喷嚏不停?毛发过敏,真不全是毛的锅
  • HBuilderX 创建 Vue3 uniCloud 项目
  • 构建AI知识库SOP:用RAG与GitCode实现品牌信息精准引用
  • DeepSeek-V3.2 二五折半年记:低价 API 到底把哪些场景做了起来
  • 使用DryadLINQ