Fox's profileFox涂鸦PhotosBlogListsMore Tools Help

Blog


    September 16

    [古文今读] 袁枚《黄生借书说》

    本文同步自游戏人生
    -------------------------------------------------------------------

    黄生允修借书。随园主人授以书而告之曰:

    “书非借不能读也。子不闻藏书者乎?七略四库,天子之书,然天子读书者有几?汗牛塞屋,富贵家之书,然富贵人读书者有几?其他祖父积、子孙弃者无论焉。非独书为然,天下物皆然。非夫人之物而强假焉,必虑人逼取,而惴惴焉摩玩之不已,日‘今日存,明日去,吾不得而见之矣。’若业为吾所有,必高束焉,庋藏焉,曰‘姑俟异日观’云尔。

    “余幼好书,家贫难致。有张氏藏书甚富。往借,不与,归而形诸梦。其切如是。故有所览辄省记。通籍后,俸去书来,落落大满,素灰丝时蒙卷轴。然后叹借者之用心专,而少时之岁月为可惜也。”

    今黄生贫类予,其借书亦类予;惟予之公书与张氏之吝书若不相类。然则予固不幸而遇张乎,生固幸而遇予乎?知幸与不幸,则其读书也必专,而其归书也必速。

    为一说,使与书俱。

    以前读书大多都是借书,工作后,俸去书来,捧卷在手也是人生一乐。况时代不同了,现在读书,既为扩充知识,也有工作需要,想读的时候,电子书翻起来还是不如纸质舒服。

    最近买书买爽了:

    9月1日,拿到了《代码大全》;

    9月11日,拿到了《UNIX网络编程》v1;

    9月16日,拿到了《TCP/IP详解》v2。

    加上之前的《TCP/IP详解》v1、v3和《UNIX环境高级编程》,Stevens的几本经典著作就剩《UNIX网络编程》v2,但因为我不喜欢中文版第二版科海培训做的那个业余的封面(看到这个封面我就想起了读大学时的那几本教材辅助),最后第一卷选了清华大学的第三版,但第二卷好像还没有。权衡再三,还是拿下了第二版。毕竟Stevens不可能再为我们写新版了。

    看了一下,只有蔚蓝书店才有这本书,为了免5元运费(满100元,是不是特傻?),加了一本《人月神话》和《人件》,虽然读大学的时候看过这两本书,但毕竟那时缺乏工程的思想,现在再读一读,还是蛮清爽的,也当收藏了。还可以弥补一下当年『黄生只能借书』的遗憾。

    后面忽然发现蔚蓝书店居然还有《UNIX编程艺术(英文版)》,只好再下一单,谁让刚才那么激动呢?本来还想把K & R的《The C Programming Language》、Bjarne Stroustrup的《The Design and Evolution of C++》和《The C++ Programming Language》一并收藏的,后来纠结要不要,因为买回来真的就成收藏了,轻易自然不会再去翻了,最终还是算了,好书看过了就看过了,没有必要非得留一本。钱钟书不是说过『假如你吃了个鸡蛋觉得不错,何必认识那下蛋的母鸡呢?』

    最后,为了凑齐新的一单,拿了本《深入理解计算机系统》,因为手上现在没有这样一本书。

    大概瞄了一下,感觉蔚蓝书店的书还要多一些,像《UNIX网络编程》、《UNIX编程艺术》各地都缺货,它那儿居然有,而且价格没有高出去,还是等拿到书再看吧。


    请移步至此阅读全文»
    September 15

    [古文今读] 梁启超《少年中国说》

    本文同步自游戏人生
    -------------------------------------------------------------------

    日本人之称我中国也,一则曰老大帝国,再则曰老大帝国。是语也,盖袭译欧西人之言也。呜呼!我中国其果老大矣乎?梁启超曰:恶,是何言!是何言!吾心目中有一少年中国在。

    欲言国之老少,请先言人之老少:老年人常思既往,少年人常思将来。惟思既往也,故生留恋心;惟思将来也,故生希望心。惟留恋也,故保守;惟希望也,故进取。惟保守也,故永旧;惟进取也,故日新。惟思既往也,事事皆其所已经者,故惟知照例;惟思将来也,事事皆其所未经者,故常敢破格。老年人常多忧虑,少年人常好行乐。惟多忧也,故灰心,惟行乐也,故盛气。惟灰心也,故怯懦;惟盛气也,故豪壮。惟怯懦也,故苟且;惟豪壮也,故冒险。惟苟且也,故能灭世界;惟冒险也,故能造世界。老年人常厌事,少年人常喜事。惟厌事也,故常觉一切事无可为者;惟好事也,故常觉一切事无不可为者。老年人如夕照,少年人如朝阳;老年人如瘠牛,少年人如乳虎;老年人如僧,少年人如侠;老年人如字典,少年人如戏文;老年人如鸦片烟,少年人如泼兰地酒;老年人如别行星之陨石,少年人如大洋海之珊瑚岛;老年人如埃及沙漠之金字塔,少年人如西伯利亚之铁路;老年人如秋后之柳,少年人如春前之草;老年人如死海之潴为泽,少年人如长江之初发源:此老年与少年性格不同之大略也。梁启超曰:人固有之,国亦宜然。

    梁启超曰:伤哉,老大也!浔阳江头琵琶妇,当明月绕船,枫叶瑟瑟,衾寒于铁,似梦非梦之时,追想洛阳尘中春花秋月之佳趣;西宫南内,白发宫娥,一灯如穗,三五对坐,谈开元、天宝间遗事,谱霓裳羽衣曲;青门种瓜人,左对孺人,顾弄孺子,忆侯门似海珠履杂遝之盛事;拿破仑之流于厄蔑,阿刺飞之幽于锡兰,与三两监守吏或过访之好事者,道当年短刀匹马,驰骋中原,席卷欧洲,血战海楼,一声叱咤,万国震恐之丰功伟烈,初而拍案,继而抚髀,终而揽镜。呜呼!面皴齿尽,白发盈把,颓然老矣。若是者舍幽郁之外无心事,舍悲惨之外无天地,舍颓唐之外无日月,舍叹息之外无音声,舍待死之外无事业,美人豪杰且然,而况于寻常碌碌者耶?生平亲友,皆在墟墓,起居饮食,待命于人,今日且过,遑知他日,今年且过,遑恤明年,普天下灰心短气之事,未有甚于老大者。于此人也,而欲望以拿云之手段,回天之事功,挟山超海之意气,能乎不能?

    呜呼!我中国其果老大矣乎?立乎今日,以指畴昔,唐虞三代,若何之郅治;秦皇汉武,若何之雄杰,汉唐来之文学,若何之隆盛;康乾间之武功,若何之烜赫;历史家所铺叙,词章家所讴歌,何一非我国民少年时代良辰美景赏心乐事之陈迹哉。而今颓然老矣,昨日割五城,明日割十城,处处雀鼠尽,夜夜鸡犬惊,十八省之土地财产,已为人怀中之肉,西百兆之父兄子弟,已为人注籍之奴,岂所谓“老大嫁作商人妇”者耶?呜呼!凭君莫话当年事,憔悴韶光不忍看,楚囚相对,岌岌顾影,人命危浅,朝不虑夕,国为待死之国,一国之民为待死之民,万事付之奈何,一切凭人作弄,亦何足怪。

    梁启超曰:我中国其果老大矣乎?是今日全地球之一大问题也。如其老大也,则是中国为过去之国,即地球上昔本有此国,而今渐渐灭,他日之命运殆将尽也;如其非老大也,则是中国为未来之国,即地球上昔未现此国,而今渐发达,他日之前程且方长也。欲断今日之中国为老大耶?为少年耶?则不可不先明国字之意义。夫国也者何物也?有土地;有人民;以居于其土地之人民而治其所居之土地之事;自制法律而自守之,有主权,有服从,人人皆主权者,人人皆服从者。夫如是斯谓之完全成立之国。地球上之有完全成立之国也,自百年以来也。完全成立者,壮年之事也;未能完全成立而渐进于完全成立者,少年之事也。故吾得一言以断之曰:欧洲列邦在今日为壮年国,而我中国在今日为少年国。

    夫古昔之中国者,虽有国之名,而未成国之形也。或为家族之国,或为酋长之国,或为诸候封建之国,或为一王专制之国,虽种类不一,要之其于国家之体质也,有其一部而缺其一部。正如婴儿自胚胎以迄成童,其身体之一二官支,先行长成,此外则全体虽粗具,然未能得其用也。故唐虞以前为胚胎时代,殷周之际为乳哺时代,由孔子而来至于今为童子时代,逐渐发达,而今乃始将入成童以上少年之界焉。其长成所以若是之迟者,则历代之民贼有窒其生机者也。譬犹童年多病,转类老态,或且疑其死期之将至焉,而不知皆由未完全未成立也。非过去之谓,而未来之谓也。

    且我中国畴昔,岂尝有国家哉,不过有朝廷耳。我黄帝子孙,聚族而居,立于此地球之上者既数千年,而问其国之为何名,则无有也。夫所谓唐、虞、夏、商、周、秦、汉、魏、晋、宋、齐、梁、陈、隋、唐、宋、元、明、清者,则皆朝名耳。朝也者,一家之私产也;国也者,人民之公产也。朝有朝之老少,国有国之老少,朝与国既异物,则不能以朝之老少而指为国之老少明矣。文、武、成、康,周朝之少年时代也;幽、厉、桓、赧、则其老年时代也。高、文、景、武,汉朝之少年时代也;元、平、桓、灵,则其老年时代也。自余历朝,莫不有之,凡此者,谓为一朝廷之老也则可,谓为一国之老也则不可。一朝廷之老且死,犹一人之老且死也,于吾所谓中国者何与焉。然则,吾中国者,前此尚未出现于世界,而今乃始萌芽云尔。天地大矣,前途辽矣,美哉,我少年中国乎!

    玛志尼者,意大利三杰之魁也。以国事被罪,逃窜异邦,乃创立一会,名曰少年意大利。举国志士,云涌雾集以应之,卒乃光复旧物,使意大利为欧洲之一雄邦。夫意大利者,欧洲第一之老大国也,自罗马亡后,土地隶于教皇,政权归于奥国,殆所谓老而濒于死者矣,而得一玛志尼,且能举全国而少年之,况我中国之实为少年时代者耶?堂堂四百余州之国土,凛凛四百余兆之国民,岂遂无一玛志尼其人者。

    龚自珍氏之集有诗一章,题曰《能令公少年行》,吾尝爱读之,而有味乎其用意之所存。我国民而自谓其国之老大也,斯果老大矣;我国民而自知其国之少年也,斯乃少年矣。西谚有之曰:“有三岁之翁,有百岁之童。”然则国之老少,又无定形,而实随国民之心力以为消长者也。吾见乎玛志尼之能令国少年也,吾又见乎我国之官吏士民能令国老大也,吾为此惧!夫以如此壮丽浓郁翩翩绝世之少年中国,而使欧西、日本人谓我为老大者何也?则以握国权者皆老朽之人也。非哦几十年八股,非写几十年白折,非当几十年差,非捱几十年俸,非递几十年手本,非唱几十年诺,非磕几十年头,非请几十年安,则必不能得一官,进一职。其内任卿贰以上,外任监司以上者,百人之中,其五官不备者,殆九十六七人也,非眼盲,则耳聋,非手颤,则足跛,否则半身不遂也。彼其一身饮食步履视听言语,尚且不能自了,须三四人在左右扶之捉之,乃能度日,于此而乃欲责之以国事,是何异立无数木偶而使之治天下也。且彼辈者,自其少壮之时,既已不知亚细、欧罗为何处地方,汉祖、唐宗是那朝皇帝;犹嫌其顽钝腐败之未臻其极,又必搓磨之,陶冶之,待其脑髓已涸,血管已塞,气息奄奄,与鬼为邻之时,然后将我二万里山河,四万万人命,一举而畀于其手。呜呼!老大帝国,诚哉其老大也。而彼辈者,积其数十年之八股、白折、当差、捱俸、手本、唱诺、磕头、请安,千辛万苦,千苦万辛,乃始得此红顶花翎之服色,中堂大人之名号,乃出其全副精神,竭其毕生力量,以保持之。如彼乞儿,拾金一锭,虽轰雷盘旋其顶上,而两手犹紧抱其荷包,他事非所顾也,非所知也,非所闻也。于此而告之以亡国也,瓜分也,彼乌从而听之,乌从而信之。即使果亡矣,果分矣,而吾今年既七十矣八十矣,但求其一两年内,洋人不来,强盗不起,我已快活过了一世矣。

    若不得已,则割三头两省之土地,奉申贺敬,以换我几个衙门;卖三几百万之人民作仆为奴,以赎我一条老命,有何不可,有何难办。呜呼!今之所谓老后、老臣、老将、老吏者,其修身、齐家、治国、平天下之手段,皆具于是矣。“西风一夜催人老,凋尽朱颜白尽头。”使走无常当医生,携催命符以祝寿,嗟乎痛哉!以此为国,是安得不老且死,且吾恐其未及岁而殇也。

    梁启超曰:造成今日之老大中国者,则中国老朽之冤业也;制出将来之少年中国者,则中国少年之责任也。彼老朽者何足道,彼与此世界作别之日不远矣,而我少年乃新来而与世界为缘。如僦屋者然,彼明日将迁居地方,而我今日始入此室处。将迁居者,不爱护其窗栊,不洁治其庭庑,俗人恒情,亦何足怪。若我少年者,前程浩浩,后顾茫茫,中国而为牛、为马、为奴、为隶,则烹脔鞭箠之惨酷,惟我少年当之;中国如称霸宇内,主盟地球,则指挥顾盼之尊荣,惟我少年享之,于彼气息奄奄,与鬼为邻者,何与焉?彼而漠然置之,犹可言也;我而漠然置之,不可言也。使举国之少年而果为少年也,则吾中国为未来之国,其进步未可量也;使举国之少年而亦为老大也,则吾中国为过去之国,其澌亡可翘足而待也。故今日之责任,不在他人,而全在我少年。少年智则国智,少年富则国富,少年强则国强,少年独立则国独立,少年自由则国自由,少年进步则国进步,少年胜于欧洲则国胜于欧洲,少年雄于地球则国雄于地球。红日初升,其道大光;河出伏流,一泻汪洋。潜龙腾渊,鳞爪飞扬;乳虎啸谷,百兽震惶。鹰隼试翼,风尘吸张;奇花初胎,矞矞皇皇。干将发硎,有作其芒。天戴其苍,地履其黄。纵有千古,横有八荒。前途似海,来日方长。美哉我少年中国,与天不老;壮哉我中国少年,与国无疆!

    “三十功名尘与土,八千里路云和月。莫等闲白了少年头,空悲切。”此岳武穆《满江红》词句也,作者自六岁时即口受记忆,至今喜诵之不衰。自今以往,弃哀时客之名,更自名曰少年中国之少年。

    1900年2月10日

    -----------------------------------------------------------------------

    欣赏任公写作此文时的英姿勃发。行文犀利,气吞万里。以前途似海的年少和天下为公的担当,面对“凋尽朱颜”的西风老朽发出自己的声音,似可涤清世间陋习恶俗。

    年少的时候,每个人都曾有一段“指点江山”的梦想。然人食五谷,终难免俗。到最后,只留下最初的文字在激励着一代一代的后来少年。

    此处发挥五千字,暂时挥发掉。只留一个记号,以后另行说明。 

    -----------------------------------------------------------------------

    成都这几天忽然就降温了,上周还要整天吹冷气,现在已经需要穿秋装了,被子还没来得及加厚,结果昨晚一觉睡得有些恍惚,早上七点醒来之后,再也没睡好,以致今天一天状态都不好。今晚早点休息,一会儿躺床上看看书就睡了。

    夏天快过去的时候,在老婆的压迫下,把含辛茹苦、呕心沥血留了两年的头发剪了。时间过去两周了,我这心里依然是痛不欲生。不过也好,以后再也不会理会她这种无理的要求了,我爱怎么留怎么留。

    blog最近作了几点改动:

    o 去掉了几个多余的分类;

    o 加了twitter @yulefox;

    o 加了豆瓣(读书) @yulefox;

    o feed使用feedburner。

    请使用GR或其他工具订阅的同学将RSS改成 http://feeds.feedburner.com/yulefox


    请移步至此阅读全文»

    ???????????(a55a4eeb-0bc5-413f-8cfa-821bd4910ae2 - 3bfe001a-32de-4114-a6b4-4005b770f6d7)

    本文同步自游戏人生
    -------------------------------------------------------------------

    ????????????????????(6c7c7a85-74c7-444e-bddc-ff5a763483b5 - 3bfe001a-32de-4114-a6b4-4005b770f6d7)


    请移步至此阅读全文»

    ???????????(a0b3a01b-7e26-4239-ba15-6848c79953cb - 3bfe001a-32de-4114-a6b4-4005b770f6d7)

    本文同步自游戏人生
    -------------------------------------------------------------------

    ????????????????????(7f37e39a-dc31-4c27-b152-ce193fc03f20 - 3bfe001a-32de-4114-a6b4-4005b770f6d7)


    请移步至此阅读全文»
    September 11

    IOCP使用时常见的几个错误

    本文同步自游戏人生
    -------------------------------------------------------------------

    在使用IOCP时,最重要的几个API就是GetQueueCompeltionStatus、WSARecv、WSASend,数据的I/O及其完成状态通过这几个接口获取并进行后续处理。

    GetQueueCompeltionStatus attempts to dequeue an I/O completion packet from the specified I/O completion port. If there is no completion packet queued, the function waits for a pending I/O operation associated with the completion port to complete.

    BOOL WINAPI GetQueuedCompletionStatus(
    __in HANDLE CompletionPort,
    __out LPDWORD lpNumberOfBytes,
    __out PULONG_PTR lpCompletionKey,
    __out LPOVERLAPPED *lpOverlapped,
    __in DWORD dwMilliseconds
    );

    If the function dequeues a completion packet for a successful I/O operation from the completion port, the return value is nonzero. The function stores information in the variables pointed to by the lpNumberOfBytes, lpCompletionKey, and lpOverlapped parameters.


    除了关心这个API的in & out(这是MSDN开头的几行就可以告诉我们的)之外,我们更加关心不同的return & out意味着什么,因为由于各种已知或未知的原因,我们的程序并不总是有正确的return & out。


    If *lpOverlapped is NULL and the function does not dequeue a completion packet from the completion port, the return value is zero. The function does not store information in the variables pointed to by the lpNumberOfBytes and lpCompletionKey parameters. To get extended error information, call GetLastError. If the function did not dequeue a completion packet because the wait timed out, GetLastError returns WAIT_TIMEOUT.


    假设我们指定dwMilliseconds为INFINITE。


    这里常见的几个错误有:


    WSA_OPERATION_ABORTED (995): Overlapped operation aborted.


    由于线程退出或应用程序请求,已放弃I/O 操作。


    MSDN: An overlapped operation was canceled due to the closure of the socket, or the execution of the SIO_FLUSH command in WSAIoctl. Note that this error is returned by the operating system, so the error number may change in future releases of Windows.


    成因分析:这个错误一般是由于peer socket被closesocket或者WSACleanup关闭后,针对这些socket的pending overlapped I/O operation被中止。


    解决方案:针对socket,一般应该先调用shutdown禁止I/O操作后再调用closesocket关闭。


    严重程度轻微易处理


    WSAENOTSOCK (10038): Socket operation on nonsocket.


    MSDN: An operation was attempted on something that is not a socket. Either the socket handle parameter did not reference a valid socket, or for select, a member of an fd_set was not valid.


    成因分析:在一个非套接字上尝试了一个操作。


    使用closesocket关闭socket之后,针对该invalid socket的任何操作都会获得该错误。


    解决方案:如果是多线程存在对同一socket的操作,要保证对socket的I/O操作逻辑上的顺序,做好socket的graceful disconnect。


    严重程度轻微易处理


    WSAECONNRESET (10054): Connection reset by peer.


    远程主机强迫关闭了一个现有的连接。


    MSDN: An existing connection was forcibly closed by the remote host. This normally results if the peer application on the remote host is suddenly stopped, the host is rebooted, the host or remote network interface is disabled, or the remote host uses a hard close (see setsockopt for more information on the SO_LINGER option on the remote socket). This error may also result if a connection was broken due to keep-alive activity detecting a failure while one or more operations are in progress. Operations that were in progress fail with WSAENETRESET. Subsequent operations fail with WSAECONNRESET.


    成因分析:在使用WSAAccpet、WSARecv、WSASend等接口时,如果peer application突然中止(原因如上所述),往其对应的socket上投递的operations将会失败。


    解决方案:如果是对方主机或程序意外中止,那就只有各安天命了。但如果这程序是你写的,而你只是hard close,那就由不得别人了。至少,你要知道这样的错误已经出现了,就不要再费劲的继续投递或等待了。


    严重程度轻微易处理


    WSAECONNREFUSED (10061): Connection refused.


    由于目标机器积极拒绝,无法连接。


    MSDN: No connection could be made because the target computer actively refused it. This usually results from trying to connect to a service that is inactive on the foreign host—that is, one with no server application running.


    成因分析:在使用connect或WSAConnect时,服务器没有运行或者服务器的监听队列已满;在使用WSAAccept时,客户端的连接请求被condition function拒绝。


    解决方案:Call connect or WSAConnect again for the same socket. 等待服务器开启、监听空闲或查看被拒绝的原因。是不是长的丑或者钱没给够,要不就是服务器拒绝接受天价薪酬自主创业去了?


    严重程度轻微易处理


    WSAENOBUFS (10055): No buffer space available.


    由于系统缓冲区空间不足或列队已满,不能执行套接字上的操作。


    MSDN: An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full.


    成因分析:这个错误是我查看错误日志后,最在意的一个错误。因为服务器对于消息收发有明确限制,如果缓冲区不足应该早就处理了,不可能待到send/recv失败啊。而且这个错误在之前的版本中几乎没有出现过。这也是这篇文章的主要内容。像connect和accept因为缓冲区空间不足都可以理解,而且危险不高,但如果send/recv造成拥堵并恶性循环下去,麻烦就大了,至少说明之前的验证逻辑有疏漏。


    WSASend失败的原因是:The Windows Sockets provider reports a buffer deadlock. 这里提到的是buffer deadlock,显然是由于多线程I/O投递不当引起的。


    解决方案:在消息收发前,对最大挂起的消息总的数量和容量进行检验和控制。


    严重程度严重


    本文主要参考MSDN


    ************* 说明 *************


    Fox只是对自己关心的几个错误和API参照MSDN进行分析,不提供额外帮助。


    请移步至此阅读全文»
    September 10

    Amdahl 定律 = Gustafson定律

    本文同步自游戏人生
    -------------------------------------------------------------------

    周伟明老师应该是多核计算领域的老人了。

    这几日因为想找找无锁(lock-free)方面的信息,就打开了周老师的blog。看到多核系统中三种典型锁竞争的加速比分析这篇文章时,觉得老师强调多核计算效率是有必要的,但拿Amdahl 定律和Gustafson定律作对比有点不恰当。

    按照我的理解,这两个定律所刻画的内容是完全一致的,只是对加速比的定义不一样罢了。这里,我们都以S(n)表示n核系统对具体程序的加速比,K表示串行部分计算时间比例。

    Amdahl 定律的加速比:S(n) = 使用1个处理器的串行计算时间 / 使用n个处理器的并行计算时间

    S(n) = 1/(K+(1-K)/n) = n/(1+(n-1)K)

    Gustafson定律的加速比:S(n) = 使用n个处理器的并行计算量 / 使用1个处理器的串行计算量

    S(n) = K+(1-K)n

    通俗的讲,Amdahl 定律将工作量看作1,有n核也只能分担1-K的工作量;而Gustafson定律则将单核工作量看作1,有n核,就可以增加n(1-K)的工作量。

    这两个计算公式都没有将锁开销考虑在内,是理想化的。周老师提到设计不当造成并行变串行的问题与这两个公式计算无关。因为任何多核计算都存在对串行和并行的设计考量,这正是程序员在使用多核并行时最关心的事情。

    总之,二者的区别只在于态度的不同:一个消极悲观,一个积极乐观,充其量是一个冷笑话,而于多核计算没有任何关联。

    我说这些也与多核计算没有关联,丝毫没有质疑多核效率的意思。相反,我期待能够通过技术层面提高多核的有效负载。

    最后一句题外话,周老师使用Word的水平一般:所有来自Word的截图都是在页面视图直接截,换行符和光标随处可见。
    请移步至此阅读全文»
    September 09

    W. R. Stevens十年

    本文同步自游戏人生
    -------------------------------------------------------------------

    W. R. Stevens是我敬仰的几位计算机领域的知名学者之一,先生辞世(1999年9月1日)已整整十年了。他留下的几部著述在世界范围内产生过并将持续产生深远的影响:

    Unix Network Programming  v1-2, 1990-1998;

    Advanced Programming in the UNIX Environment, 1992;

    TCP/IP Illustrated V1-3, 1994-1996.

    其中,UNP原定出版三卷,先生英年早逝,这不能不说是一个遗憾。所幸,除TCP外,UNP和APUE均有人续写或编写新版。

    读书的时候虽然粗浅学习过TCP,但离开学校之后也就渐渐忘却了,而对TCP实战经验也仅仅停留在Win API使用上。这次因工作和兴趣使然,慢慢发现理论的效用,希望可以将这几本经典之作细细读一读。

    上次在书店只是购回了TCP v1, v3,这几日终于看到UNPv1(cn E3)到货,立马入手,顺便把TCP v2一起买了。

    晚上在家便翻看一会儿,大热天的,说不上如沐春风,怎么也算是苦热中的一件乐事了。


    请移步至此阅读全文»
    September 01

    ACE: Socket封装(01)

    本文同步自游戏人生
    -------------------------------------------------------------------

    o *__ 序 __* o

    在阅读ACE代码和C++NPv1, v2, APG的时候,我意识到一个问题:虽然稍有C++和网络基础的同学都可以读懂ACE,但如果你对OS(五大管理模块都包含在内)、TCP/IP、C++、Design Patterns了解越多,你就越能体会ACE为什么需要这么庞杂,虽然它不够完美(但至少我还没有资格来批评这一点,我现在最常想做的一个动作就是五体投地)。

    而且我隐约感觉到,我现在所写的很多东西在以后(对于有些人或许就是现在)看来会相当不深刻、相当不严谨,但对于一段学习历程,这个过程是必然的、必需的。

    在C++NPv1中,Douglas C. Schmidt把原始socket及其API的缺陷有些妖魔化了,比如一段加上注释、空行在内的35行的代码,被指出有10处错误之多。这就像很多其他语言的倡导者或反传统C/C++指针者在批评指针时的说法一样。长期使用原始socket和指针的同学对此感觉很不舒服,何况socket API提供了大量错误检测的接口,至多是不够友好罢了。你好就好了,没必要抓住别人一顿痛批吧,『本是同根生,相煎何太急』。

    虽然Solaris、Linux的很多版本及Windows对起源于Berkeley的socket API进行了重写,但不可否认,由于历史原因和POSIX标准的存在,对于使用者而言,我们可以无视这些API的实现差异。只是一旦我们从socket通信扩展到其他IPC通信的话,就需要正视各种I/O细节的差异了。

    由于UNIX中,对于socket, file, pipe, device的大多数操作,描述符都是通用的(这一点,OS上面讲的更清楚些)。而Windows中,句柄大多不能互换(socket对于MS来说是舶来品)。系统和标准的不一致导致地址、协议和API的混杂甚至混乱。

    UNIX下的描述符和Windows的句柄可以看作是同一个概念,只是应用环境不一样,所描述的内容也时常不一样,再简单了说,它们都是一个整型的ID。

    ACE的源码中使用了大量预处理指令,尤其在跨平台/编译环境的部分更加明显。鉴于C/C++标准的博大胸怀,有些指令需要阅读相关编译器提供的帮助文档:

    o #pragma: GCC, MSVC

    o #define (#, #@, ##) : GCC, MSVC

    其中有若干代码文件以.inl为后缀,里面是对部分函数的内联实现,以使代码结构看上去更加简洁。如果确定使用内联函数的话,*.inl将被包含于*.h的最后,如果不使用,则像*.h一样,包含于*.cpp的头部。

    ACE采用doxygen输出文档,在阅读代码注释时能够感受到差异,但基本不会影响阅读。

    o * __ 关于第3章(C++NPv1)__ * o

    ACE抽象的地址类ACE_Addr拥有ACE_DEV_Addr, ACE_FILE_Addr, ACE_INET_Addr, ACE_SPIPE_Addr, ACE_UNIX_Addr五个子类。对于狭义上的网络通信(TCP/IP)而言,ACE_INET_Addr对应于我们熟悉的sockaddr_in。

    ACE_IPC_SAP是IPC(interprocess communication)I/O操作类的root类。

    从编码的角度看,这个类漂亮的地方在于示例了抽象类的另一种实现方式。

    一提到抽象类,大多数人的第一反应是pure virtual function。当一个基类确定需要使用virtual function时,这是一个不错的选择。但我们都知道虚拟函数有开销。而且对于一个结构简单的抽象基类和其继承子类(尤其是大量使用时),一个虚函数表带来的开销会让整个设计显得十分蹩脚。

    我们都知道如何强制让一个类无法使用default constructor(protected)。如果对基类使用该方法,仅使子类具有public的default constructor,这就达到了定义抽象基类的效果。

    virtual destructor的意义在于防止delete父类指针(指向子类对象)时未调用子类destructor。在此例中,为避免这种情况,同样将destructor声明为protected即可。

    从设计实现的角度看,相较于socket API,ACE_IPC_SAP的子类ACE_SOCK提供了编译时对句柄合法性的检测。

    从逻辑功能层面划分,socket有三种角色:

    o active connection role (connector):主动连接

    o passive connection role (acceptor):被动连接

    o communication role (stream):数据通信

    但socket API毕竟不是OOD出来的,对于一个socket描述符,也完全没有必要去限制其担负的功能,更不可能搞成三种不同的socket。而OOD的ACE则可以轻易实现对socket对象及其操作的封装。

    工厂类ACE_SOCK_Connector是一个主动创建通信端的工厂类。socket API中的connect接口只是为一个socket建立与其它peer的网络连接,而不产生新的socket实例,也不依赖于任何其它socket。同样,ACE_SOCK_Connector只是为一个ACE_SOCK_Stream对象(对用于数据通信的socket的封装)连接到ACE_Addr(对struct sockaddr的封装)提供接口,也不含对ACE_SOCK_Stream对象的其它操作。

    工厂类ACE_SOCK_Acceptor是一个被动创建通信端的工厂类。当监听到新的网络连接后,为该连接初始化一个ACE_SOCK_Stream对象。和connector不同的是,acceptor依赖于一个已经存在的充当监听功能的socket句柄(ACE_SOCK),因此,ACE_SOCK_Acceptor是ACE_SOCK的一个子类。

    ACE_SOCK_Stream是只负有通信传输功能的socket,对应connection-oriented的TCP通信格式stream,和UDP的CE_SOCK_CODgram相呼应。ACE_SOCK_Stream只是socket的通信载体,在两个工厂ACE_SOCK_Connector和ACE_SOCK_Acceptor中初始化。这样一个类除支持最基本的数据发送(send)和接收(recv)和阻塞(blocking)、非阻塞(nonblocking)及定时(timed)的I/O模式外,还支持分散读取(scatter-read)和集中写入(gather-write)。

    对于一个简单的『网络课程作业:写一个有连接的IM小程序』,上面这些内容已经足够了。当然即使使用对应的几个socket API也已经足够了。但我们显然更加关心如此庞大的一个库,是如何解决复杂的网络应用的,我尤其关心的是多线程并发如何更好的处理。

    所以,我准备跑到第8、9章了。


    请移步至此阅读全文»