认真你就输了

Feed Rss

最近搞了很多简易HTTP服务器端,参考了各种代码。

今天在参考nanohttpd写我自己的java版HTTP服务器时,发现nanohttpd的示例程序发出的response接收起来很慢。

研究了一下代码,貌似是因为nanohttpd最近刚刚merge了一个别人写的gzip格式输出的pull request,但没有好好检查,导致返回Content-Encoding是gzip的response时,既没有添加Content-Length,又没有使用chunked Transfer-Encoding,这再加上keep-alive,根本就是断了浏览器感知response长度的一切手段,难怪chrome收他的response这么慢,人家只能傻傻的等你服务器端超时关闭连接了啊。

添加一行代码,在gzip输出时启用chunked格式,响应时间立刻从5s降到37ms。我看这次改动简洁易懂,有实践证据,也有理论支撑,把握挺大的,就鼓起勇气发出了我个人账户的第一个pull request。

这次虽然是第一次用私人账户发pull request,但其实我们公司管理代码就是用github,我在公司里常常要发pull request,所以也没什么新鲜感了。

听说有拖延症的人怕自己完不成任务,所以尽量少接任务,结果完成效率还是不尽如人意。

结构化拖延理论认为,有拖延倾向就更应该多接任务,这样为了逃避任务A而去做任务B,不知不觉就能搞定大部分工作了。

所以我要挖新坑,哼哼。

最近在看很多通信协议的东西,也需要看Unix网络编程。但网络编程这东西,没有个具体的需求的话,我也不知道这么些函数,这些模型都适合什么场景。

为了给自己一个练手的空间,这次准备尝试一下对实时网络通信质量要求非常高的领域——MOBA类游戏。

以往做的游戏都是websocket就能搞定的小玩意儿,一点儿也感觉不到各种网络通信方式之间的性能差别。MOBA类游戏有一个最重要也最容易分辨的质量标准,就是延迟。UDP到底比TCP快多少?怎么能在保证快速响应和在有丢包风险的连接上保证逻辑正确呢?还是实践出真理吧。

学L2tp,要先看PPP,直接看PPP的rfc担心直接看陌生领域的英文文献会晕,所以先找了本网络协议分析的中文教材看了起来。

可这个教材也不是直接教完整的PPP的,只是讲了几页纸,后面就是简介各种协议,稍微详细地讲了TCP/IP。

我想L2tp也是要为上层的IP协议服务的嘛,先看看IP协议总没有坏处。可这些协议哪个都是充满了细节,一起看下来需要相当的时间。

这种一直读纯理论,不能上机实验的学习方法,在大学时代就被验证了是很容易磨灭学习热情的。上次两天20小时读完HTTP协议已经是我的忍耐极限了,读完之后我一口气写了一星期的代码才缓过劲来。

这次的vpn项目难度不小,要做好持久战的准备,不能期待像HTTP一样一口气吃下来。那怎么打持久战呢?就是注意回复能力。用LOL的话讲就是要有回血赖线的能力。

读教材看文档,吸收纯理论知识是混线涨经验,但是会消耗精神力,相当于对线期的损血。那写代码加深理解,稳固住刚刚吸收进来,根基还不牢靠的知识,让我不用每时每刻消耗大量精神力去把他们暂存在脑内存中,就是磕血瓶回血了。

目前我经验涨了一些,但是血线有点低了,现在不能贪线上兵,要先稳一稳,猥琐塔下回回血吧。

好,那接下来的目标就转向实际动手环节吧。

另外除了这个系列以外,我可能还会开一个自己动手写小HTTP Server的项目。目标是写一个纯java的Http代理,初步是先实现接收Http请求的部分。参考nanoHttpd和mongoose项目。

L2TP(RFC2661)上来就说自己是扩展PPP协议(RFC1661)的,我看我还是先看一遍PPP协议吧。

而PPP协议是一个数据链路层协议,比TCP低两层,比IP协议都低一层。用普通的socket编程不能直接修改包头,所以我需要借助于WinPcap和libpcap库来获得对数据链路层的控制能力。

数据链路层啊,难怪比HTTP难懂,搞这东西要和Sniffer一样用WinPcap,这一点就让我觉得很高端了。

这种超级底层的冷门知识,估计对我的游戏开发工作是没有任何帮助的。不过毕竟工程师,拿这种阳春白雪的东西当业余爱好装个B也是好的。继续看吧。

RFC是什么,维基上有解释。按我的理解,就是一些通信、网络方面的协议和规则,有人把讨论后的结果整理成格式统一的文档,大家都遵守这个文档的话,各自做的软件之间就可以交流了。

举例来说,我上个月读了一份RFC2616,是HTTP 1.1协议,也就是我们现在最广泛使用着的一版HTTP协议。里面定义了客户端(浏览器之类)应该以什么格式内容发送请求(HTTP  Request)给服务器端,服务器端根据自己的情况,应该返回什么格式内容的响应(HTTP Response)。

最开始读这份RFC是因为我需要写一个HTTP代理程序,又毫无头绪,需要了解一些相关知识。

其实在读RFC之前,我尝试性地让代理把接收到的所有请求内容一字不差地转发给服务器端,再把从服务器端收到的内容全文转发给客户端,心想这就是标准的端口转发了吧,结果是代理运行起来一卡一卡的,而且卡得很有规律,每10秒可以读取一批请求,然后要卡10秒,才能读取下一批请求。因为对HTTP协议一无所知,所以当撞大运编程失败以后,我就只能老老实实的去看协议了。

在我花了20小时读完别人翻译的中文版RFC2616以后,我发现我爱上了RFC这个东西。

 

首先,RFC是第一手资料,非常有价值。

你想和别人探讨问题,他举出某HTTP教材里的内容,你举出RFC里面的内容,那结果肯定是你的证据说服力更强。毕竟写教材的人也是看完RFC去写的教材,教材已经是第二手知识了。

其次,RFC的格式严谨,而且只有干货没有废话,上来就可以大流量地吸收新知识,读起来很痛快。

我至今读过的技术书籍里,都没有任何一本有RFC这样清晰易懂的内容组织。不过拿一群人整理出来的RFC和一两个人写的技术教材来做比较,也是有点欺负人就是了。

 

读完RFC之后,再去写我的HTTP代理程序就如鱼得水了。因为目的明确,省去了很多摸索和撞大运的时间。

这种感觉就好象在游戏公司,程序员进项目组的第一天就被告知“游戏策划和美术音乐素材已经全部就绪,就等着您去编程了”时那种痛快的感觉。虽然我从来没体验过。

现在我的HTTP代理已经可以正常运转。虽然实现很简单很不守规矩,但并不影响使用。等下周把keep-alive也给实现了,在主要功能方面我就没有遗憾了。

 

关于RFC,HTTP代理这件事完成的很不错,接下来就要去找下一个战场了。

正好hacklu同学有一个需要搭建l2tp配ipsec的vpn的需求,而我又一直无法突破拖延心境去找资料设置这个东西。干脆,我自己试着写一个l2tp配ipsec的服务器端吧!

在公司用了这么久的git,这次拿到github上去试试身手。如果这次也能顺利搞定,那我就考虑定期攻克一个RFC文档了。

 

还是老规矩,先通读相关RFC,再设计和编程。

相关RFC有:

1. RFC2661 《Layer Two Tunneling Protocol "L2TP"》

2. RFC3193 《Securing L2TP using IPsec》

3. RFC2402 《IP Authentication Header》

4. RFC2406 《IP Encapsulating Security Payload (ESP)》

5. RFC791 《INTERNET PROTOCOL》

6. RFC2409 《The Internet Key Exchange (IKE)》

7. RFC1661 《The Point-to-Point Protocol (PPP)》

8. RFC1962 《The PPP Compression Control Protocol (CCP)》

...

卧槽,这个比HTTP代理程序复杂好多啊……依我挖大坑的经验,这种陨石坑我是填不上的。不过难得有一个可以让我学点东西的目标,不试试就放弃,也太可惜了。

hacklu用练字来磨练自己,那我就用啃RFC来磨练自己吧!

还可以趁机写一些RFC阅读笔记当日志,充充数量。毕竟最近几年的日志产量少得我都不好意思了。

百度空间也走了

05.07.2015, 6 条评论, 未分类, by , 1,610 views.
百度空间关闭

百度空间关闭

又一个我认真待过的日志空间关闭了,上次是msn space。

我虽然很久不用百度空间了,但对他一直很有亲切感。提起百度空间,就会想起我的整个大学生涯,兼一半的留学生涯,我的第一批“网友”,我的一百多幅画,我初学编程时为一点小成就而喜悦,我意志薄弱时任性的吐槽发牢骚。

好东西为什么总是死的早?也许不是他很好,而是我对他产生了感情,感情深到同类产品的优势不足以抵消我对老朋友的留恋。确实,相比探险与开拓,我更倾向于恋家和守旧。

msn space, google reader, baidu空间,还有fileden网络硬盘,大的还可以算上verycd,115,以前的迅雷,我没用过的像google wave,google code,google buzz(...google你是关闭了多少业务= =),delicious书签,等等。现在我还用着的evernote,dropbox,youtube,gmail,weibo,qq,xiami,github... 考虑到现在复杂的大环境,我越来越觉得把所有数据都管理在自己手里是很重要的。

现在我家里有一台synologyNAS(3TB,800MHz,128MB),一台HP小服务器(500G+3TB,2*2.2GHz,4GB),有一个同学的VPS的账号,一个同学的网页服务器的账号。

作为一个业余爱好,我想把我需要的数据都管理在自己手边。新业务出来了,只要把自己的数据导入导出一下就可以用,定期把网络上的数据同步回本地永久保存。嗯,能做到就好了。不过其实这事情本身意义不大,还是玩的成分居多。

不过,等我年纪再大一点,想回顾自己年轻时的足迹时,发现百度已经把日志彻底删除了,微博已经被新社交环境淘汰了,动画漫画已经没地方下了,就该骂现在的我为什么不勤快点把收集做好吧。所以这事情要说有意义呢,也算是有点意义。等闲下来好好计划一下,在github上开一个收集各平台数据的爬虫,和在本地管理各种数据的框架的坑吧^_^

程序也是,画画也是,日语英语,科研,都是二半吊子,不上不下的。
这几项我比初学者都强太多,想找水平相当的人一起学吧,找不到。
做到这些领域的业内名人的大有人在,也好找,可他们比我强太多,也聊不到一块儿去。
寂寞。
大学时代三年画室生活让我彻底领教到学习环境的重要性。有一批水平跟自己差不多的人在一起学,有一个老师会根据你当前的表现和做法适时提出评价和建议甚至示范,这种环境带来的学习效率让我震惊。之后我一直很在意寻找这种学习环境,可惜成功再也无法复制,我只好继续自己擅长的自学之路。

其实自学能做到的事情是很有限的。不是说我自学能力不强,事实上我身上最强大的属性就是快速的正确自学的能力。无论是大学时代考前突击拿一等奖学金,还是留学前自学过托福过入学考试,都是我强大自学能力的体现。但你看像画画这件事,在去画室之前我大概自己凭兴趣临摹漫画有500小时左右,当时感觉已经是画得很好了,视野范围内无敌手,周围的同学也是一片赞美,但现在回头一看就知道当时我的水平和视野是多么的有限。

我的自学能力仅限于知识领域,也就是看书就会的部分。画画的理论部分其实非常简单,我从画室也只是学到了物体分光影,重点刻画明暗交界线,定位时利用虚拟水平垂直辅助线进行肉眼测量等等,三两句就能说完。画技的提升,靠的是手感,是实战经验。但你闷头画效率也不会很高,因为你不知道应该注意什么,提高什么,不知道障碍的突破点是什么,甚至根本不知道是什么障碍。那种憋着一股子劲儿却只能打在棉花上的无力感会很快耗尽我的热情。

热情是我保持行动力的核心燃料,非常珍贵。有老师指导可以很节能的持续学习,还外带一个固定的学习时间带,保证了一个稳定的学习时间,反正特别特别好。现在想找老师带我画CG,可惜怎么找也找不到了。在网上看了一圈在线授课的东西,学员学完以后依然水平太低,看得我害怕。老师画画是真有水平,但是上课只是自己在那边画画,再自言自语几句,或者回答一下学生问题。只是这样的话,网上发自己作画过程的高手千千万,我为什么要花上万人民币看你一个人在那直播呢?画完以后的作品点评也非常没有意义,这种点评到处都可以要到,对高手们来说只是举手之劳而已。既然授课,就要有最核心的“在我作画的过程中给出评价,甚至中途接手修改,改过后给我接着画”的服务。没有这一条的所谓授课都是耍无赖,没有参与价值。

当年对画画一窍不通的时候被各种“大师”的教程教材坑苦了,现在虽然学会了绕过各种陷阱,却遗憾地发现已经找不到适合我的学习环境了。或者说,找不到比我自学的效率高很多的学习环境了。

所以我继续自学画画不就好了么?
其实自学成才的人很多,我的程序也完全是自学成材的。但人是有属性倾向的,我就是写程序消耗的燃料少,画画消耗的燃料多,燃料的追加时机和追加数量是随机的,这就是我的属性。两件事都让我很快乐,很有成就感,都是我乐于花一生时间培育的手艺。现在程序方面顺风顺水,画画这边卡住了。以励志小故事的观点来看,这里我应该克服自己的惰性,修改自己的燃耗属性,逆天改命,翻盘取胜。而实际上呢,我还是会一如既往地等待某个转折条件的到来,等待一个改变当前局面的契机(而不是奇迹)。我是很固执很不听人劝的,改变自己远远难过改变环境。在至今为止几乎所有的人生重要转折点上,我都是靠“改变环境”来取胜的。当然这里有点夸张的成分,毕竟有些改变真的只是运气好而已=w=。

接下来怎么办呢?接下来我依然会细心照顾现在进展顺利的编程分支,并不断为自己制造可能激发我绘画热情的机会,等待其中某个机会真正生效,然后一举翻盘~嗯?怎么听起来不太像改变环境?哎,反正是【在不改变我自己对学习环境严苛挑剔性的前提下使自己可以继续画画】 ==>【 创造一个让自己满意的环境】==>【改变现有坏境】的感觉啦~!

我是很偏执固执,又睿智强大的,能驾驭这种性格的话,我的产出可以抵上一个小队。为了能随心所欲的使用自己的能力,我得看点心理学基础,考个二级心理咨询师什么的了。真期待看到将来和我一起生活的人会怎样习得“我”的使用方法(笑)。

公司没有写服务器端的经验,之前用某服务器端引擎提供商提供的示例代码改改出了一个游戏。这次轮到我当服务器端程序(一个人负责C++全部,哈哈),看原来的代码没用到的示例功能特别多,很碍眼,想重写一个清爽简洁版的服务器端,但是这需要好好理解哪些代码可以删,哪些不能动,偏偏引擎是闭源的,我只有header和示例的注释可以看,初始化时七八个管道啊连接器什么的实在猜不出是干什么用的,读所有那些header也很花时间,犹豫了。
从吸收知识的角度来说,肯定是好好理解一下比较好。但是我毕竟是拿钱工作的,消化任务列表的速度太慢,可能被公司添加新人进来——其实已经听说准备招人加入服务器端开发了——我独占服务器端开发的时间有限,在别人来以前尽量多推进一些也许比较好?比如公司看我一个人也写得挺快,就让我自己干了什么的。我这段时间刚投了简历准备跳槽,挺希望能在走之前给这个项目搭好稳固的地基(最好干脆让我干完再走更有成就感)。反正就是各种时间不够用。偏偏我上一个项目的php工作太悠闲,已经很难集中精神读大段代码了(写还是没什么问题,写比较容易投入)。坐在座位上开开微博,看看邮箱,干着急就是不去读代码。
现在也是,我看我实在是不肯干,就索性出来写写日志放松一下,也让文字帮助我思考,看看这个问题到底出在哪。
这个服务器端怎么写,我心里大致已经有数了,上周也刚写好对战部分提交了。今天开始要写游戏房间、匹配等等进入战斗前的部分。虽然代码还没细看,但我们有rpc定义文件,仔细读了一遍,知道客户端可以调用哪些功能,对这个匹配服务器怎么做也就有了大致的印象。就像笃志说的,架构一看就懂的感觉。
架构虽然懂了,落到实现还是很犹豫的,毕竟实现方法太多了。而我又好多想,希望在架构设计阶段就把之前困扰我的服务器端调试功能也搞定,这下更不敢动手了。其实服务器端调式,我的终极理想是做成跨平台,让vs也能编译运行服务器端,这样调试就不是问题了。不过我们的引擎商只给了我一个老版的windows端lib,只有linux版的.a是最新的,这链接上去谁知道跑得对不对呢。还有我们现在的服务器端主要靠引擎商的示例代码,里面直接用到了fork函数,这个我真是没法在windows下模拟,平台跨不起来。要么大改逻辑,但那样工时就会花很多,上头问下来我说我搞个人研究重构着玩儿掉了5个工作日什么的,那还得了。我现在能做的,就是一层一层的分开,让这个项目尽可能多的部分可以被windows编译运行。战斗部分因为可以独立运行,现在已经实现在windows下运行了,不过debug还是不是很顺手,看来只有vs也不能解决问题。我们公司是没有这方面的经验,真没有,连gdb的存在和用法都是我教他们的,在那以前他们完全靠log来debug服务器端。
好了,再说回来,这个服务器端调试不顺手。因为客户端和服务器端通信有一个顺序和时间间隔,我想做一个操作顺序就要改一次客户端代码,不方便。这里我的理想解决方案肯定是导入lua,毕竟以后也需要测试机器人这一点已经确认了,早晚要做这个功能。不过做测试机器人的任务有它自己的工时,现在做是占用我做匹配服务器的工时,不好。另外还有一点,是不能同时停下两边。一边下了断点停下了,另一边很容易超时。尤其gdb我玩得还不是很得心应手,比起下条件断点,我更常做的是最开始就下好断点,然后每次被断了就continue,直到我想要的那个条件的到来。说是每次,其实也就两三次啦,但客户端那边是有固定时序的。我每次打continue或者c的时候,时间误差都会累积,最后时序就不是我预期的样子了。这一点真的烦,习惯了vs上开发单机游戏时尽在掌握的调试能力,现在是各种憋屈恼火。服务器端调试已经是太钝的斧头了,需要我磨刀一下才不会耽误今后大量砍柴的任务。当然,这个耗费的工时只能自己挤出来了。问题是,我肯出这个时间,也不知道该怎么改良。如果断下来之前先通知另一端,就需要一个类似break()函数,里面是rpc通信,但这就失去了断点可以实时添加的便利。我每加一个断点,都要重新编译一遍的话,我宁可自己读代码找bug。一个想法是,两边的超时行为应该默认不发生,直到我手动发出一个超时信号,才出发当前阶段的超时。这样一边进入断点也不用着急,算是为调试专门设计的环境,可能需要定义特别的编译宏来开关这个功能什么的。为了今后的等待也能被这个调试方式管理,程序中所有的超时管理应该使用统一的方法。在服务器端,我估计会有控制时间流这方面的需求,所以已经做了一个GameTimer类,游戏中所有的时间都是询问GameTimer来获得的。那么调试模式下,每次启动GameTimer时它都不动,直到收到debug超时信号,就一口气走过一个巨大的数字,保证什么逻辑都超时。甚至debug超时信号中可以自带走过的时间,让时间的流动尽在掌握。嗯,听起来有点靠谱了,不愧是靠写字思考的男人,写博客果然有效,哈哈。
哎,我就喜欢琢磨这些基础建设,因为以后都能从中获益。游戏逻辑的具体实现是写过就算,对我来说属于用过即弃的代码,没有沉淀,没什么兴趣。

 

工作几乎没有,每天到公司看一天网页,写一点点代码,偶尔改几行bug,到点走人,回家玩6小时lol,凌晨3点睡觉。
这种生活也许称得上清闲,40年后过这样的生活的话我也许会满意。可问题是现在的我完全不能承受这种被搁置的生活,每时每刻都是煎熬。
我心里还是那个迫切需要吸收新知识新技术来维持快乐和满足的年轻人,但我的无力也显而易见——我很难扭转已经步入轨道的生活状态。借口也好什么也好,现在的事实就是如此。
现在手里唯一可以抗衡这股惯性的武器,就是“6月底完成demo和求职简历一起投出去”的这个决定。每天6小时lol,别说demo了,清醒地工作这个底线都快完不成了,但是有“截止日期”这个倒计时加速器在,我对完成demo还是抱有一定自信的。结果可能是各种妥协,比如放弃移动平台,只做html5版,甚至只放截图和描述,不提供在线访问的连接等等,但肯定会在妥协到某一步的时候完成demo。毕竟是有着“每一届寒暑假都能且只能在最后一天(在有一定偷懒的基础上)完成作业”的传奇般的男人,哇——哈哈哈哈。
在截止日期发挥功效之前的这段低谷,就让我用lol(和伏特加)麻醉自己吧!

/* 

一个混淆数字userid的简单方法

效果是 encode: 1234 => 48915174

decode : 48915174 => 1234

*/ 

function encodeNumber( $number, $modulo, $prime ){
    return ( $number * $prime ) & $modulo; // &操作等价于取模
}

function decodeNumber( $code, $modulo, $primeInverse ){
    return ( $code * $primeInverse ) & $modulo; // &操作等价于取模
}

// 例:
// magic number 可以用在线工具事先算出来 http://planetcalc.com/3311/
$modulo = pow( 2, 26 ) - 1;
$prime = 7381371;
$primeInverse = 5555;

$id = 1234;
function show( $number ){
    global $modulo, $prime, $primeInverse;
    $encoded = encodeNumber( $number, $modulo, $prime );
    $decoded = decodeNumber( $encoded, $modulo, $primeInverse );
    print_r( [ 
        "id" => $number, 
        "encoded" => $encoded,
        "decoded" => $decoded,
    ] );
}
show( $id );

----------------  代码文字分隔线  ----------------

有兴趣可以在 http://www.compileonline.com/execute_php_online.php 上演练一下,复制粘贴就好。

简单介绍一下。

出处是这里,我是从stackoverflow上看来的。原理是利用模逆元素的特性,blablabla。
模用2的幂减1是为了求模计算的效率。

限制是可以混淆的数字不能大于模,否则编码后的数值会有重复,或者说会从头开始新一轮循环。
另外这毕竟不是加密,用途类似base64吧,不过不会像base64那样一眼被人识破然后被轻易解读。