谈一谈网l编E学习经?/p>
陈硕
giantchen@gmail.com
blog.csdn.net/Solstice
2011-06-08
PDF 版下载:https://github.com/downloads/chenshuo/documents/LearningNetworkProgramming.pdf
本文谈一谈我在学习网l编E方面的一些个人经验?#8220;|络~程”q个术语的范围很q,本文指用Sockets API开发基于TCP/IP的网l应用程序,具体定义?#8220;|络~程的各UQ务角?#8221;一节?/p>
受限于本人的l历和经验,q篇文章的适应范围是:
· x86-64 Linux服务端网l编E,直接或间接?Sockets API
· 公司内网。不一定是局域网Q但M位于公司防火墙之内,环境可控
本文可能不适合Q?/p>
· PC客户端网l编E,E序q行在客LPC上,环境多变且不可控
· Windows|络~程
· 面向公网的服务程?/p>
· 高性能|络服务?/p>
本文分两个部分:
1. |络~程的一些胡思ؕ惻I谈谈我对q一领域的认?/p>
2. 几本必看的书Q基本上q是W. Richard Stevents那几?/p>
另外Q本文没有特别说明时均暗指TCP协议Q?#8220;q接”?#8220;TCPq接”Q?#8220;服务?#8221;?#8220;TCP服务?#8221;?/p>
|络~程的一些胡思ؕ?/h3>
以下胡ؕ列出我对|络~程的一些想法,前后无关联?/p>
|络~程是什么?
|络~程是什么?是熟l用Sockets API吗?说实话,在实际项目里我只用过两次Sockets APIQ其他时候都是用封装好的网l库?/p>
W一ơ是2005q在学校做一个羽毛球赛场计分pȝQ我用C# ~写q行在PCZ的YӞ负责比分的显C;再用C# 写了q行在PDA上的计分界面Q记分员拿着PDA记录比分Q这两部分程序通过 TCP协议怺通信。这其实是个单的分布式系l,体育馆有不止一片场圎ͼ每个场地都有一名拿PDA的记分员Q每个场地都有两台显C比分的PC机(昄器是42吋^板电视,攑֜场地的对角,q样两边看台的观众都能看到比分)。这两台PC机功能不完全一P一台只负责昄当前比分Q另一台还要负责与PDA通信Qƈ更新数据库里的比分信息。此外,q有一台PC责周期性地从数据库d全部7片场地的比分Q显C在体育馆墙上的大屏q上。这台PC上还q行着一个程序,负责生成比分数据的静态页面,通过FTP上传发布到某门户|站的体育频道。系l中q有一个录入赛E(参赛队,q动员,出场序{)数据库的E序Q运行在数据库服务器上。算下来整个pȝ有十来个E序Q运行在二十多台讑֤QPC和PDAQ上Q还要考虑可靠性。将来有Z把这个小pȝ仔细讲一Ԍ挺有意思的?/p>
q是我第一ơ写实际目中的|络E序Q当时写下来的感觉是像写命o行与用户交互的程序:E序在命令行输出一句提CQ等待客戯入一句话Q然后处理客戯入,再输Z一句提CQ如此@环。只不过q里?#8220;客户”不是人,而是另一个程序。在建立好TCPq接之后Q双方的E序都是read/write循环Qؓ求简单,我用的是blockingdQ,直到有一Ҏ开q接?/p>
W二ơ是2010q编写muduo|络库,我再ơ拿起了Sockets APIQ写了一个基于Reactor模式的C++ |络库。写q个库的目的之一是惌日常的网l编E从Sockets API的琐细节中解脱出来Q让E序员专注于业务逻辑Q把旉用在刀刃上。Muduo |络库的CZ代码包含了几十个|络E序Q这些示例程序都没有直接使用Sockets API?/p>
在此之外Q无论是实习q是工作Q虽然我写的E序都会通过TCP协议与其他程序打交道Q但我没有直接用过Sockets API。对于TCP|络~程Q我认ؓ核心是处?#8220;三个半事?#8221;Q见《Muduo |络~程CZ之零Q前a》中?#8220;TCP |络~程本质?#8221;。程序员的主要工作是在事件处理函C实现业务逻辑Q而不是和Sockets API较劲?/p>
q里q是没有说清?#8220;|络~程”是什么,Ll阅d?#8220;|络~程的各UQ务角?#8221;?/p>
学习|络~程有用吗?
以上说的是比较底层的|络~程Q程序代码直接面对从TCP或UDP收到的数据以及构造数据包发出厅R在实际工作中,另一U常?的情冉|通过各种 client library 来与服务端打交道Q或者在现成的框架中填空来实现serverQ或者采用更上层的通信方式。比如用libmemcached与memcached打交道,使用libpq来与PostgreSQL 打交道,~写Servlet来响应httphQ用某URPC与其他进E通信Q等{。这些情况都会发生网l通信Q但不一定算?#8220;|络~程”。如果你的工作是前面列D的这些,学习TCP/IP|络~程q有用吗Q?/p>
我认是有必要学一学,臛_在troubleshooting 的时候有用。无论如何,q些library或framework都会调用底层的Sockets API来实现网l功能。当你的E序遇到一个线上问题,如果你熟悉Sockets APIQ那么从strace不难发现E序卡在哪里Q尽可能你没有直接调用q些Sockets API。另外,熟悉TCP/IP协议、会用tcpdump也大大有助于分析解决U上|络服务问题?/p>
在什么^C学习|络~程Q?/h4>
对于服务端网l编E,我徏议在Linux上学习?/p>
如果?0q前Q这个问题的{案或许是FreeBSDQ因为FreeBSDҎ苗红Q在2000q那一ơ互联网潮中扮演了重要角色Q是很多公司首选的免费服务器操作系l?000q那会儿Linuxq远未成熟,qepoll都还没有实现。(FreeBSD?001q发?.1版,加入了kqueueQ从此C10k不是问题。)
10q后的今天,事情起了变化QLinux成ؓ了市Z额最大的服务器操作系l?http://en.wikipedia.org/wiki/Usage_share_of_operating_systems)。在Linuxq种大众pȝ上学|络~程Q遇C么问题会比较Ҏ解决。因为用的h多,你遇到的问题别h多半也遇到过Q同样因为用的h多,如果真的有什么内核bugQ很快就会得C复,臛_有work around的办法。如果用别的pȝQ可能一个问题发到论坛上半个月都不会有h理。从内核源码的风格看QFreeBSD更干净整洁Q注释到位,但是无奈它的市场份额q不如LinuxQ学习Linux是更好的技术投资?/p>
可移植性重要吗Q?/h4>
写网l程序要不要考虑UL性?q取决于目需要,如果贵公司做的程序要卖给其他公司Q而对方可能用Windows、Linux、FreeBSD、Solaris、AIX、HP-UX{等操作pȝQ这时候考虑UL性。如果编写公司内部的服务器上用的|络E序Q那么大可只x一个^収ͼ比如Linux。因为编写和l护可移植的|络E序的代L当高Q^台间的差异可能远比想象中大,即便是POSIXpȝ之间也有不小的差异(比如Linux没有SO_NOSIGPIPE选项Q,错误的返回码也大不一栗?/p>
我就不打把muduo往Windows或其他操作系l移植。如果需要编写可UL的网l程序,我宁愿用libevent或者Java Nettyq样现成的库Q把脏活累活留给别h?/p>
|络~程的各UQ务角?/h4>
计算机网l是?big topicQ涉及很多h物和角色Q既有开发h员,也有q维人员。比方说Q公司内部两台机器之?ping 不通,通常ql运lh员解冻I看看是布U有问题q是路由器设|不对;两台机器能ping通,但是E序q不上,l检查是本机防火墙设|有问题Q通常ql管理员解决Q两台机器能q上Q但是丢包很严重Q发现是|卡或者交换机的网口故障,q件维修h员解冻I两台机器的程序能q上Q但是偶发q去的请求得不到响应Q通常是程序bugQ应该由开发h员解冟?/p>
本文主要兛_开发h员这一角色。下面简单列Z些我能想到的跟网l打交道的编EQ务,其中前三Ҏ面向|络本nQ后面几Ҏ在计机|络之上构徏信息pȝ?/p>
1. 开发网l设备,~写防火墙、交换机、\由器的固?firmware
2. 开发或UL|卡的驱?/p>
3. UL或维护TCP/IP协议栈(特别是在嵌入式系l上Q?/p>
4. 开发或l护标准的网l协议程序,HTTP、FTP、DNS、SMTP、POP3、NFS
5. 开发标准网l协议的“附加?#8221;Q比如HAProxy、squid、varnish{web load balancer
6. 开发标准或非标准网l服务的客户端库Q比如ZooKeeper客户端库Qmemcached客户端库
7. 开发与公司业务直接相关的网l服务程序,比如x聊天软g的后台服务器Q网游服务器Q金融交易系l,互联|企业用的分布式量存储Q微博发帖的内部q播通知Q等{?/p>
8. 客户端程序中涉及|络的部分,比如邮g客户端中?POP3、SMTP通信的部分,以及|游的客LE序中与服务器通信的部?/p>
本文所指的“|络~程”专指W?,卛_TCP/IP协议之上开发业务Y件?/p>
面向业务的网l编E的特点
跟开发通用的网l程序不同,开发面向公怸务的专用|络E序有其特点Q?/p>
· 业务逻辑比较复杂Q而且时常变化
如果写一个HTTP服务器,在大致实现HTTP /1.1标准之后Q程序的M功能一般不会有太大的变化,E序员会把时间放在性能调优和bug修复上。而开发针对公怸务的专用E序Ӟ功能说明书(specQ很可能不如HTTP/1.1标准那么l致明确。更重要的是Q程序是快速演化的。以x聊天工具的后台服务器ZQ可能第一版只支持在线聊天Q几个月之后发布W二版,支持ȝ消息Q又q了几个月,W三版支持隐w聊天;随后Q第四版支持上传头像Q如此等{。这要求E序员能快速响应新的业务需求,公司才能保持竞争力?/p>
· 不一定需要遵循公认的通信协议标准
比方说网游服务器没什么协议标准,反正客户端和服务端都是本公司开发,如果发现目前的协议设计有问题Q两边一h了就是了?/p>
· E序l构没有定论
对于高ƈ发大吞吐的标准网l服务,一般采用单U程事g驱动的方式开发,比如HAProxy、lighttpd{都是这个模式。但是对于专用的业务pȝQ其业务逻辑比较复杂Q占用较多的CPU资源Q这U单U程事g驱动方式不见得能发挥现在多核处理器的优势。这留给E序员比较大的自由发挥空_做好了横扫千军,做烂了一败涂地?/p>
· 性能评判的标准不?/p>
如果开发httpdq样的通用服务Q必然会和开源的Nginx、lighttpd{高性能服务器比较,E序员要投入相当的精力去优化E序Q才能在市场上占有一席之地。而面向业务的专用|络E序不一定有开源的实现以供Ҏ性能Q程序员通常更加注重功能的稳定性与开发的便捷性。性能只要一代比一代强卛_?/p>
· |络~程起到支撑作用Q但不处于主导地?/p>
E序员的主要工作是实C务逻辑Q而不只是实现|络通信协议。这要求E序员深入理解业务。程序的性能瓉不一定在|络上,瓉有可能是CPU、Disk IO、数据库{等Q这时优化网l方面的代码q不能提高整体性能。只有对所在的领域有深入的了解Q明白各U因素的权衡(trade-off)Q才能做Z些有针对性的优化?/p>
几个术语
互联|上的很多口水战是由对同一术语的不同理解引LQ比我写的《多U程服务器的适用场合》就曄说是“挂羊头卖狗肉”Q因文章中丄 master例子“Ҏq不上是个|络服务器。因为它的瓶颈根本就跟网l无兟?#8221;
· |络服务?/p>
“|络服务?#8221;q个术语实含义模糊Q到底指gq是软gQ到底是服务于网l本w的机器Q交换机、\由器、防火墙、NATQ,q是利用|络为其他h或程序提供服务的机器Q打印服务器、文件服务器、邮件服务器Q。每个hҎ自己熟悉的领域,可能会有不同的解诅R比方说或许有h认ؓ只有支持高ƈ发高吞吐的才是|络服务器?/p>
Z避免无谓的争执,我只?#8220;|络服务E序”或?#8220;|络应用E序”q种含义明确的术语?#8220;开发网l服务程?#8221;通常不会造成误解?/p>
· 客户端?服务端?
在TCP|络~程里边Q客L和服务端很容易区分,d发vq接的是客户端,被动接受q接的是服务端。当Ӟq个“客户?#8221;本n也可能是个后台服务程序,HTTP Proxy对HTTP Server来说是个客L?/p>
· 客户端编E?服务端编E?
但是“服务端编E?#8221;?#8220;客户端编E?#8221;׃那么好区分。比?Web crawlerQ它会主动发起大量连接,扮演的是HTTP客户端的角色Q但g应该归入“服务端编E?#8221;。又比如写一?HTTP proxyQ它既会扮演服务?#8212;—被动接受 web browser 发v的连接,也会扮演客户?#8212;—d?HTTP server 发vq接Q它I竟服务端q是客户端?我猜大多Ch会把它归入服务端~程?/p>
那么I竟如何定义“服务端编E?#8221;Q?/p>
服务端编E需要处理大量ƈ发连接?也许是,也许不是。比如云风在一介l网游服务器的博?a style="color: #0066aa; text-decoration: none; ">http://blog.codingnow.com/2006/04/iocp_kqueue_epoll.html中就谈到Q网怸用到?#8220;q接服务?#8221;需要处理大量连接,?#8220;逻辑服务?#8221;只有一个外部连接。那么开发这U网?#8220;逻辑服务?#8221;服务端~程q是客户端编E呢Q?/p>
我认为,“服务端网l编E?#8221;指的是编写没有用L面的长期q行的网l程序,E序默默地运行在一台服务器上,通过|络与其他程序打交道Q而不必和人打交道。与之对应的是客L|络E序Q要么是短时间运行,比如wgetQ要么是有用L面(无论是字W界面还是图形界面)。本文主要谈服务端网l编E?/p>
7x24重要吗?内存片可怕吗Q?/h4>
一谈到服务端网l编E,有h立刻会提?x24q行的要求。对于某些网l设备而言Q这是合理的需求,比如交换机、\由器。对于开发商业系l,我认求程?x24q行通常是系l设计上考虑不周。具体见《分布式pȝ的工E化开发方法》第20v。重要的不是7x24Q而是在程序不必做?x24的情况下也能辑ֈ_高的可用性。一个考虑周到的系l应该允许每个进E都能随旉启,q样才能在廉L服务器硬件上做到高可用性?/p>
既然不要?x24Q那么也不必x内存碎片,理由如下Q?/p>
· 64-bitpȝ的地址I间_大,不会出现没有_的连l空间这U情c?/p>
· 现在的内存分配器Qmalloc及其W三方实玎ͼ今非昔比Q除了memcachedq种U以内存为卖点的E序需要自p计分配器之外Q其他网l程序大可用系l自带的malloc或者某个第三方实现?/p>
· Linux Kernel也大量用C动态内存分配。既然操作系l内栔R不怕动态分配内存造成片Q应用程序ؓ什么要x?
· 内存片如何度量Q有没有什么工兯为当前进E的内存片状况评个分?如果不能比较两种Ҏ的内存碎片程度,谈何优化Q?/p>
有hZ避免内存片Q不使用STL容器Q也不敢new/deleteQ这是premature optimizationq是因噎废食呢?
协议设计是网l编E的核心
对于专用的业务系l,协议设计是核心Q务,军_了系l的开发难度与可靠性,但是q个领域q没有Ş成大家公认的设计程?/p>
pȝ中哪个程序发赯接,哪个E序接受q接Q如果写标准的网l服务,那么q不是问题,按RFC来就行了。自p计业务系l,有没有章法可循?以网ؓ例,到底是连接服务器dq接逻辑服务器,q是逻辑服务器主动连?#8220;q接服务?#8221;Q似乎没有定论,两种做法都行。一般可以按?#8220;依赖->被依?#8221;的关pL设计发vq接的方向?/p>
比新接难的是关闭q接。在传统的网l服务中Q特别是短连接服务)Q不是服务端主动关闭连接,比如daytime、HTTP/1.0。也有少部分是客Ld关闭q接Q通常是些长连接服务,比如 echo、chargen{。我们自q业务pȝ该如何设计连接关闭协议呢Q?/p>
服务端主动关闭连接的~点之一是会多占用服务器资源。服务端d关闭q接之后会进入TIME_WAIT状态,在一D|间之内hold住一些内核资源。如果ƈ发访问量很高Q这会媄响服务端的处理能力。这g暗示我们应该把协议设计ؓ客户端主动关闭,让TIME_WAIT状态分散到多台客户机器上,化整为零?/p>
q又有另外的问题Q客L赖着不走怎么办?会不会造成拒绝服务dQ或许有一个二者结合的ҎQ客L在收到响应之后就应该d关闭Q这h TIME_WAIT 留在客户端。服务端有一个定时器Q如果客L若干U钟之内没有d断开Q就t掉它。这样善意的客户端会把TIME_WAIT留给自己Qbuggy的客L会把 TIME_WAIT留给服务端。或者干脆用长q接协议Q这样避免频J创建销毁连接?/p>
比连接的建立与断开更重要的是设计消息协议。消息格式很好办QXML、JSON、Protobuf都是很好的选择Q难的是消息内容。一个消息应该包含哪些内容?多个E序怺通信如何避免race conditionQ见《分布式pȝ的工E化开发方法》p.16的例子)Q系l的全局状态该如何跃迁Q可惜这斚w可供参考的例子不多Q也没有太多通用的指导原则,我知道的只有30q前提出的end-to-end principle和happens-before relationship。只能从实践中慢慢积累了?/p>
|络~程的三个层?/h4>
侯捷先生在《O談程序員與編E》中讲到 STL q用的三个档ơ:“會用STLQ是一E檔ơ。對STL原理有所了解Q又是一個檔ơ。追y過STL源碼Q又是一個檔ơ。第三種檔次的h用v STL 來,虎虎生風之勢i非W一檔次的h能夠望其項背?#8221;
我认为网l编E也可以分ؓ三个层次Q?/p>
1. 读过教程和文?/p>
2. 熟悉本系lTCP/IP协议栈的脾气
3. 自己写过一个简单的TCP/IP stack
W一个层ơ是基本要求Q读q《Unix|络~程》这L~程教材Q读q《TCP/IP详解》基本理解TCP/IP协议Q读q本pȝ的manpage。这个层ơ可以编写一些基本的|络E序Q完成常见的d。但|络~程不是照猫画虎q么单,若是按照manpage的功能描q就能编写品的网l程序,那h生就太幸了?/p>
W二个层ơ,熟悉本系l的TCP/IP协议栈参数设|与优化是开发高性能|络E序的必备条件。摸透协议栈的脾气还能解军_作中遇到的比较复杂的|络问题。拿Linux的TCP/IP协议栈来_
· 有可能出现自q接Q见《学之者生Q用之者死——ACE历史与简评》D的三个硬伤)Q程序应该有所准备?/p>
· Linux的内怼有bugQ比如某UTCP拥塞控制法曄出现TCP window clampingQ窗口箝位)bugQ导致吞吐量暴跌Q可以选用其他拥塞控制法来绕开(work around)q个问题?/p>
q些阴暗角落在manpage里没有描qͼ要通过其他渠道了解?/p>
~写可靠的网l程序的关键是熟悉各U场景下的error codeQ文件描q符用完了如何?本地ephemeral port暂时用完Q不能发hq接怎么办?服务端新建ƈ发连接太快,backlog用完了,客户端connect会返回什么错误?Q,有的在manpage里有描述Q有的要通过实践或阅L码获得?/p>
W三个层ơ,通过自己写一个简单的TCP/IP协议栈,能大大加深对TCP/IP的理解,更能明白TCPZ么要q么设计Q有哪些因素制约Q每一步操作的代h是什么,写v|络E序来更是成竹在胸?/p>
其实实现TCP/IP只需要操作系l提供三个接口函敎ͼ一个函敎ͼ两个回调函数。分别是Qsend_packet()、on_receive_packet()、on_timer()。多q前有一文章《用libnet与libpcap构造TCP/IP协议软g》介l了在用h实现TCP/IP的方法。lwIP也是很好的借鉴对象?/p>
如果有时_我打自己写一个Mini/Tiny/Toy/Trivial/Yet-Another TCP/IP。我准备换一个思\Q用TUN/TAP讑֤在用h实C个能与本机点对点通信的TCP/IP协议栈,q样那三个接口函数就表现为我最熟悉的文件读写。在用户态实现的好处是便于调试,协议栈做成静态库Q与应用E序链接CP库的接口不必是标准的Sockets APIQ。做完这一版,q可以l发挥,用FTDI的USB-SPI接口芯片q接ENC28J60适配器,做一个真正独立于操作pȝ的TCP/IP stack。如果只实现最基本的IP、ICMP Echo、TCP的话Q代码应能控制在3000行以内;也可以实现UDPQ如果应用程序需要用到DNS的话?/p>
最主要的三个例?/h4>
我认为TCP|络~程有三个例子最值得学习研究Q分别是echo、chat、proxyQ都是长q接协议?/p>
Echo的作用:熟悉服务端被动接受新q接、收发数据、被动处理连接断开。每个连接是独立服务的,q接之间没有兌。在消息内容斚wEcho有一些变U:比如做成一问一{的方式Q收到的h和发送响应的内容不一Pq时候要考虑打包与拆包格式的设计Q进一步还可以写简单的HTTP服务?/p>
Chat的作用:q接之间的数据有交流Q从a收到的数据要发给b。这样对q接理提出的更高的要求Q如何用一个程序同时处理多个连接?fork() per connectiong是不行的。如何防止串话?b有可能随时断开q接Q而新建立的连接c可能恰好复用了b的文件描q符Q那么a会不会错误地把消息发lcQ?/p>
Proxy的作用:q接的管理更加复杂:既要被动接受q接Q也要主动发赯接,既要d关闭q接Q也要被动关闭连接。还要考虑两边速度不匹配,见《Muduo |络~程CZ之十Qsocks4a 代理服务器》?/p>
q三个例子功能简单,H出了TCP|络~程中的重点问题Q挨着做一遍基本就能达到层ơ一的要求?/p>
TCP的可靠性有多高Q?/h4>
TCP?#8220;面向q接的、可靠的、字节流传输协议”Q这里的“可靠”I竟是什么意思?《Effective TCP/IP Programming》第9条说QRealize That TCP Is a Reliable Protocol, Not an Infallible ProtocolQ那么TCP在哪U情况下会出错?q里说的“出错”指的是收到的数据与发送的数据不一_而不是数据不可达?/p>
我在《一U自动反消息类型的 Google Protobuf |络传输Ҏ》中设计了带check sum的消息格式,很多CZ理解Q认为是多余的。IP header里边有check sumQTCP header也有check sumQ链路层以太|还有CRC32校验Q那么ؓ什么还需要在应用层做校验Q什么情况下TCP传送的数据会出错?
IP header和TCP header的check sum是一U非常弱?6-bit check sum法Q把数据当成反码表示?6-bit integersQ再加到一赗这Uchecksum法能检Z些简单的错误Q而对某些错误无能为力Q由于是单的加法Q遇?#8220;?#8221;不变的情况就无法查出错误Q比如交换两?6-bit整数Q加法满交换律Q结果不变)。以太网的CRC32只能保证同一个网D上的通信不会出错Q两台机器的|线插到同一个交换机上,q时候以太网的CRC是有用的Q。但是,如果两台机器之间l过了多U\由器呢?

上图中Client向Server发了一个TCP segmentQ这个segment先被装成一个IP packetQ再被封装成ethernet frameQ发送到路由器(图中消息aQ。Router收到ethernet frame (b)Q{发到另一个网D?c)Q最后Server收到dQ通知应用E序。Ethernet CRC能保证a和b相同Qc和d相同QTCP header check sum的强度不以保证收发payload的内容一栗另外,如果把Router换成NATQ那么NAT自己会构造cQ替换掉源地址Q,q时候a和d的payload不能用tcp header checksum校验?/p>
路由器可能出现硬件故障,比方说它的内存故障(或偶焉误)D收发IP报文出现多bit的反转或双字节交换,q个反{如果发生在payload区,那么无法用链路层、网l层、传输层的check sum查出来,只能通过应用层的check sum来检。这个现象在开发的时候不会遇刎ͼ因ؓ开发用的几台机器很可能都连到同一个交换机Qethernet CRC能防止错误。开发和试的时候数据量不大Q错误很隑֏生。之后大规模部v到生产环境,|络环境复杂Q这时候出个错p人措手不及。有一论文《When the CRC and TCP checksum disagree》分析了q个问题。另外《The Limitations of the Ethernet CRC and TCP/IP checksums for error detection?http://noahdavids.org/self_published/CRC_and_checksum.html)也值得一诅R?/p>
q个情况真的会发生吗Q会的,Amazon S3 ?008q?月就遇到q,单bit反{D了一ơ严重线上事故,所以他们吸取教训加?check sum。见http://status.aws.amazon.com/s3-20080720.html
另外一个例证:下蝲大文件的时候一般都会附上MD5Q这除了有安全方面的考虑Q防止篡改)Q也说明应用层应该自p法校验数据的正确性。这是end-to-end principle的一个例证?/p>
三本必看的书
谈到Unix~程和网l编E,W. Richard Stevens 是个l不开的h物,他生前写?本书QAPUE、两卷UNP、三卷TCP/IP。有四本与网l编E直接相兟뀂UNPW二卷其实跟|络~程关系不大Q是APUE在多U程和进E间通信(IPC)斚w的补充。很多h把TCP/IP一二三卷作为整体推荐,其实q三本书用处不同Q应该区别对待?/p>
q里谈到的几本书都没有超出孟岩在《TCP/IP |络~程之四书五l》中的推荐,说明|络~程q一领域已经相对成熟E_?/p>
· ?em>TCP/IP Illustrated, Vol. 1: The Protocols》中文名《TCP/IP 详解》,以下U?TCPv1?/p>
TCPv1 是一本奇书?/p>
q本书迄今至被三百多篇学术论文引用q?a style="color: #0066aa; text-decoration: none; ">http://portal.acm.org/citation.cfm?id=161724。一本学术专著被论文引用不上出奇,隑־的是一本写l程序员看的技术书能被学术论文引用几百ơ,我不知道q有哪本技术书能做到这一炏V?/p>
TCPv1 堪称 TCP/IP领域的圣l。作?W. Richard Stevens 不是 TCP/IP 协议的发明hQ他从用者(E序员)的角度,?tcpdump 为工P?TCP 协议抽丝剥茧娓娓道来Q第17~24章)Q让人叹服。恐?TCP 协议的设计者也难以讲解得如此出Ԍ臛_不会像他q么耐心l致地画几百q收?package 的时序图?/p>
TCP作ؓ一个可靠的传输层协议,其核心有三点Q?/p>
1. Positive acknowledgement with retransmission
2. Flow control using sliding windowQ包括Nagle 法{)
3. Congestion controlQ包括slow start、congestion avoidance、fast retransmit{)
W一点已l以满?#8220;可靠?#8221;要求Qؓ什么?Q;W二ҎZ提高吞吐量,充分利用链\层带宽;W三Ҏ防止q蝲造成丢包。换a之,W二Ҏ避免发得太慢Q第三点是避免发得太快,二者相互制U。从反馈控制的角度看QTCP像是一个自适应的节阀Q根据管道的拥堵情况自动调整阀门的量?/p>
TCP?flow control 有一个问题,每个TCP connection是彼此独立的Q保存有自己的状态变量;一个程序如果同时开启多个连接,或者操作系l中q行多个|络E序Q这些连接似乎不知道他h的存在,~少对网卡带宽的l筹安排。(或许C的操作系l已l解决了q个问题Q)
TCPv1 唯一的不x它出版太早了Q?993 q至今网l技术发展了几代。链路层斚wQ当q主的 10Mbit |卡和集U器早已l被淘汰Q?00Mbit 以太|也没什么企业在用了Q交换机(switch)也已l全面取代了集线?hub)Q服务器机房?1Gbit |络ZQ有些场合甚至用上了 10Gbit 以太|。另外,无线|的普及也让TCP flow control面新挑战;原来设计TCP的时候,Z认ؓ丢包通常是拥塞造成的,q时应该放慢发送速度Q减L塞;而在无线|中Q丢包可能是信号太弱造成的,q时反而应该快速重试,以保证性能。网l层斚w变化不大QIPv6 雷声大雨点小。传输层斚wQ由于链路层带宽大增QTCP window scale option 被普遍用,另外 TCP timestamps option ?TCP selective ack option 也很常用。由于这些因素,在现在的 Linux 机器上运?tcpdump 观察 TCP 协议Q程序输Z与原书有些不同?/p>
一个好消息QTCPv1于今年10月(2011q_推出W二版,Amazon 的预定页面是Q?a style="color: #0066aa; text-decoration: none; ">http://www.amazon.com/gp/product/0321336313Q让我们拭目以待?/p>
· 《Unix Network Programming, Vol. 1: Networking API》第二版或第三版Q这两版的副标题E有不同Q第三版L?XTIQ,以下l称 UNPQ如果需要会?UNP2e、UNP3e l分?/p>
UNP是Sockets API的权威指南,但是|络~程q不是用那十几个Sockets API那么单,作?W. Richard Stevens深刻地认识到q一点,他在UNP2e的前a中写刎ͼhttp://www.kohala.com/start/preface.unpv12e.html
I have found when teaching network programming that about 80% of all network programming problems have nothing to do with network programming, per se. That is, the problems are not with the API functions such as accept and select, but the problems arise from a lack of understanding of the underlying network protocols. For example, I have found that once a student understands TCP's three-way handshake and four-packet connection termination, many network programming problems are immediately understood.
搞网l编E,一定要熟悉TCP/IP协议及其外在表现Q比如打开和关闭Nagle法Ҏ发包的媄响)Q不然出Ҏ料之外的情况摸不着头脑了。我不知道ؓ什么UNP3e在前a中去掉了q段臛_重要的话?/p>
另外值得一提的是,UNP中文版翻译得相当好,译者杨l张先生是真懂网l编E的?/p>
UNP很详l,面面俱到QUDP、TCP、IPv4、IPv6都讲C。要说有什么缺点的话,是太详l了Q重点不够突出。我十分赞同孟岩说的
“Q孟岩)我主张,在具备基之后Q学习Q何新东西Q都要抓住主U,H出重点。对于关键理论的学习Q要集中_֊Q速战速决。而旁枝末节和非本质性的知识内容Q完全可以留l实践去零敲打?/p>
“原因是这LQQ何一个高U的知识内容Q其中都只有一部分是有思想创新、有重大影响的,而其它很多东襉K是琐的、非本质的。因此,集中学习时必L握住真正重要那部分,把其它东西留l实c对于重点知识,只有集中学习其理论,才能保体系性、连贯性、正性,而对于那些旁枝末节,只有边干边学能够让你了解它们的真实h值是大是,才能让你留下更生动的印象。如果你把精力用错了地方Q比如用集中大块的时间来学习那些本来只需要查查手册就可以明白的小技巧,而对于真正重要的、思想性东西放在^旉敲碎打,那么肯定是事倍功半,甚至适得其反?/p>
“因此我对于市面上l大部分开发类图书都不?#8212;—它们基本上都是面向知识体pLw的Q而不是面向读者的。L把相关的所有知识细节都攑֜一堆,然后一堆一堆攒h变成一本书。反映在内容上,是毫无重点地^铺直叙,不分轻重地陈q细节,往往在第三章以前q无聊的细节谋杀了读者的热情。ؓ什么当q侯捷先生的《深入浅出MFC》和 Scott Meyers ?nbsp;Effective C++ 能够成ؓl典Q就在于q两本书抓住了各自领域中的主qԌ提纲挈领Q纲丄张,一下子打通读者的ȝ二脉。可惜这L书太,q是已?Richard Stevens 和当?Jeffrey Richter 的书Q也只是在体pL和深入性上高h一_q不是面向读者的书?#8221;
什么是旁枝末节呢?拿以太网来说QCRC32如何计算是“旁枝末节”。网l程序员要明白check sum的作用,知道Z么需要check sumQ至于具体怎么CRC׃需要程序员操心。这部分通常是由|卡g完成的,在发包的时候由g填充CRCQ在收包的时候网卡自动丢弃CRC不合格的包。如果代码里边确实要用到CRC计算Q调用通用的zlibpQ也不用自己实现?/p>
UNP像l了你一堆做菜的原料Q各USockets 函数的用法)Q常用和不常用的都给了(Out-of-Band Data、Signal-Driven IO {等Q,要靠读者自p法取舍组合,做出一盘大菜来。在W一遍读的时候,我徏议只读那些基本且重要的章节;另外那些ơ要的内容可略作了解Q即便蟩q不M无妨。UNP是一本操作性很强的书,读这本这本书一定要上机l习?/p>
另外QUNP丄两个例子Q菜谱)太简单,daytime和echo一个是短连接协议,一个是长连接无格式协议Q不以覆盖基本的网l开发场景(比如 TCP包与拆包、多q接之间交换数据Q。我估计 W. Richard Stevens 原打在 UNPW三卷中讲解一些实际的例子Q只可惜他英q早逝,我等无福阅读?/p>
UNP是一本偏重Unix传统的书Q这本书写作的时候服务端q不需要处理成千上万的q接Q也没有现在那么多网l攻凅R书中重点介l的以accept()+fork()来处理ƈ发连接的方式在现在看来已l有点吃力,q本书的代码也没有特别防范恶意攻凅R如果工作涉及这些方面,需要再q一步学习专门的知识QC10k问题Q安全编E)?/p>
TCPv1和UNP应该先看哪本Q我不知道。我自己是先看的TCPv1Q花了大U半学期旉Q然后再读UNP2e和APUE?/p>
· ?em>Effective TCP/IP Programming?/p>
W三本书我犹豫了很久Q不知道该推荐哪本,q有哪本书能?W. Richard Stevens 的这两本比肩吗?W. Richard Stevens 为技术书c的写作树立了难以逾越的标杆,他是一位伟大的技术作家。没能看C写完 UNP W三卷实在是人生的遗憾?/p>
?em>Effective TCP/IP Programming》这本书属于专家l验ȝc,初看时觉得收获很大,工作一D|间再看也能有新的发现。比如第6 ?#8220;TCP是一个字节流协议”Q看q这一条就不会ȝI所谓的“TCP_包问题”。我手头q本电力C?001q的中文版翻译尚可,但是很狗血的是把参考文献去掉了Q正文中引用的文章资料根本查不到名字。h?011q重新翻译出版的版本有参考文献?/p>
其他值得一看的?/h4>
以下两本都不易读Q需要相当的基础?/p>
· ?em>TCP/IP Illustrated, Vol. 2: The Implementation》以下简U?TCPv2
1200늚大部_详细讲解?.4BSD的完整TCP/IP协议栈,注释?5,000行C源码。这本书啃下来不ҎQ如果时间不充裕Q我认ؓ没必要啃完,应用层的|络E序员选其中与工作相关的部分来阅读卛_?/p>
q本书第一作者是Gary WrightQ从叙述风格和内容组l上是典型的“面向知识体系本n”Q先讲mbufQ再从链路层一路往上、以太网、IP|络层、ICMP、IP多播、IGMP、IP路由、多播\由、Socketspȝ调用、ARP{等。到了正文内?/4的地Ҏ开始讲TCP。面面俱到、主ơ不明?/p>
对于主要使用TCP的程序员Q我认ؓTCPv2一大半内容可以跌不看Q比如\p、IGMP{等Q开发网l设备的人可能更兛_q些内容Q。在工作中大可以把IP视ؓhost-to-host的协议,?#8220;IP packet如何送达Ҏ机器”的细节视为黑盒子Q这不会影响对TCP的理解和q用Q因为网l协议是分层的。这L下来Q需要看的只有三四百,四五千行代码Q大大减M负担?/p>
q本书直接呈现高质量的工业操作pȝ源码Q读h有难度,L它甚臌?#8220;不求甚解的能?#8221;。其一Q代码只能看Q不能上行,也不能改动试验。其二,与操作系l其他部分紧密关联。比如TCP/IP stack下接|卡驱动、Y中断Q上承inode转发来的pȝ调用操作Q中间还要与q的进E文件描q符理子系l打交道Q如果要把每一部分都弄清楚Q把持不住就q失主题了。其三,一些历史包p代码变复杂晦涩。比如BSD?0q代初需要在只有4M内存的VAX上实现TCP/IPQ内存方面捉襟见肘,q才发明了mbufl构Q代码也增加了不偶发复杂度Qbuffer不连l的处理Q?/p>
读这套TCP/IP书切忌胶柱鼓瑟,q套书以4.4BSD为底Q其描述的行为(特别是与timer相关的行为)与现在的Linux TCP/IP有不的出入Q用书本上的知识直接套用到生产环境的Linuxpȝ可能会造成不小的误解和困扰。(TCPv3不重要,可以成套买来收藏Q不M可。)
· ?em>Pattern-Oriented Software Architecture Volume 2: Patterns for Concurrent and Networked Objects》以下简UPOSA2
q本书ȝ了开发ƈ发网l服务程序的模式Q是对UNP很好的补充。UNP中的代码往往把业务逻辑和Sockets API调用混在一P代码固然短小_悍Q但是这U编码风格恐怕不适合开发大型的|络E序。POSA2模块化,|络通信交给library/frameworkdQ程序员写代码只x业务逻辑Q这是非帔R要的思想。阅读这本书对于深入理解常用的event-driven|络库(libevent、Java Netty、Java Mina、Perl POE、Python Twisted{等Q也很有帮助Q因些库都是依照q本书的思想~写的?/p>
POSA2的代码是C意性的Q思想很好Q细节不佟뀂其C++ 代码没有充分考虑资源的自动化理(RAII)Q如果直接按照书中介l的方式d现网l库Q那么会l用者造成不小的负担与陷阱。换a之,照他说的做,而不是照他做的学?/p>
不g看的?/h4>
Douglas Comer 教授名气很大Q著作等w,但是他写的网l方面的书不g读,呛_D。网l编E与 TCP/IP 斚wQ有W. Richard Stevens 的书扛鼎Q计机|络原理斚wQ有Kurose?#8220;自顶向下”和Peterson?#8220;pȝ”打旗Q没其他Z么事ѝ顺便一提,Tanenbaum的操作系l教材是最好的之一Q嗯Q之二,因ؓ他写了两本:“C”?#8220;设计与实?#8221;Q,不过他的计算机网l和体系l构教材的地位比不上他的操作pȝ书的C。体pȝ构方面,Patterson ?Hennessy二h合作的两本书是最好的Q近q来崭露头角的《深入理解计机pȝ》也非常好;当然Q侧重点不同?/p>
(?
posted on 2011-06-06 08:44 陈硕 阅读(13435) 评论(9) ~辑 收藏 引用 所属分c? muduo