兼济天下则达 独善其身则穷
终于买了一台Mac电脑,发现Mac电脑确实不错。许许多多的快捷键,可以提高效率。很方便的一点是,每次开机会启动到上次关机的状态,保证工作的连续性。而笔记本方便携带,即便回老家也可以继续写代码。
最近一个多月,陆陆续续完成了从Wordpress迁移到Django,还是很高兴的。之所以离开Wordpress,一个重要的原因是对PHP不熟悉,想对Wordpress进行修改时总感觉力不从心。虽然对Django也不熟悉,但毕竟Django是用Python写的,而我对于Python的熟悉程度远远超过PHP.
于是开始动手,其实2014年的时候就想迁移过一次,只是后来不了了之。现在重新动手,还是有些基础,而网络上也有许多用Django搭建博客的例子。渐渐的功能一步步完善,也就成了现在这个样子,代码地址https://github.com/dengshilong/dengshilong。以下是一些体会。
下面是一些参考链接:
很久很久以前,看《康美之恋》的电视广告时,就被它唯美的风格所吸引,之后也看过一些类似的MV,于是想做一个网站来记录下这些MV。之前学了Django后,尝试用来做一个博客系统,并有了一个雏形。有了这个基础后,顺手把MV小站也做好了。
花了一个上午的时间,就写好了。功能不需要太多,只要能记录MV就行,这都是得益于Django的后台功能,而MV的视频源都是来自优酷。不想写评论功能,因为这样的话,代码就需要更多了,于是用了社会化评论。
MV小站可移步至http://lemonbean.info/
真没有想到,距离上次更新已经将近三个月了,看来真的是越来越拖拉了。
记得上次说,将使用MVC来重新设计网站,添加评论功能,RSS功能。后来发现,写一个MVC框架也没有那么容易,还是用Smarty相对容易一些,于是用Smarty来实现数据和显示分离。
对于评论功能,一直纠结是否要实现像Wordpress那种嵌套评论功能,写着写着,发现嵌套功能也没有那么容易,于是只好实现最简单的评论功能。而对于评论,本来还打算实现分页功能的,而在我看来,实现这个功能一共有两种办法,一种是重新刷新整个页面,另一种是使用Ajax。使用Ajax的体验相对好一些,可是我又发现,实现这个Ajax功能也并不容易,后来又考虑到网站那可怜的访问量,评论根本上不去,所以也没有必要实现这么复杂的功能,于是干脆连分页都不做了。而对于RSS,访问量上不去,这功能估计也用不到,先不写了,一切从简吧。
这次改版,一个最大的变化还是左导航栏,有了这个问题题目的导航栏,看题目内容就很快了。这还得多谢颖妍同学。
如果感兴趣的话,可移步欧拉工程中文站
知道欧拉工程,是在任晓祎的博客里,之后断断续续也做了一些题目,并被数学之美所折服。其中的许多问题,如果没有一个合理的解决方法,即使高性能的计算机也无能为力。典型的一题是素数的判断,记得微软实习面试时也问过这道题,当时还以为很简单,没想到这后面还隐藏着米勒拉宾算法,并且竟然会与费马小定理搭上边。
但是这个理由还不足以需要自己做一个网站,以及将题目翻译成中文。之所以这么做,出于两个原因。第一个是,想学习怎么翻译,提高英文水平;第二个是,学习如何做网站。
高三的时候,一次英文课上,老师叫我和几个同学扮演一个采访节目,其中我扮演阿迪力,也就是那个走钢丝的特技演员。当时我的口语水平很烂,所以放弃这次扮演,于是老师笑道,阿迪力不懂英语啊。当时很羞愧,学了五年的英语,口语竟然这么烂。但同时,我也觉得这个老师不好,怎么能这么打击学生呢?事实上,我的英语阅读水平一直都很不错,即使读起原版书籍也毫不吃力,只是我这人生性腼腆,不常开口说英语,以至于口语很烂。后来学日语的经历也证明了这一点,日语老师韩凌燕很耐心的教我们从发音开始学起,而当时自己学的很努力,最后日语的发音也还不赖,事实上,在普通话,英语,日语中,我的日语发音是最标准的。而为了证明我还是懂英语的,后来看了许多英文书,英语的阅读水平显著提高。为了学以致用,决定翻译欧拉工程,一方面锻炼英语,另一方面,也可以加深对题目的理解。
事实上,不会做网站一直都是我的一个心病,所以能够自己独立做一个网站一直是我的一个愿望。两年前找实习时,就问过余晟老师,如果要做Web开发要从何做起。余晟老师说,先从最简单的网站开始,从前端到后台,功能,设计全部走一遍,而后台语言的选择最好是PHP。当时还和余老师说,自己喜欢Python多一些,还是不学PHP了,因此还被骂了一顿。工作之后,看志容用Django做的博客,于是决定用Django做网站。可是后来发现并没有那么容易,很多时候都在学习Django如何处理Web开发,无法按照自己的想法做。于是还是听取余老师的意见,用PHP,在看了哈佛大学的《构建动态网站》以及李航用PHP写的博客,更加坚定了学习PHP的想法,其中Malan老师说,PHP就是为Web而生的,它为Web开发提供了许多语言特性。于是开始用PHP做这个网站。
不知道从什么时候开始做了,陆陆续续的用空闲的时间做。先从如何编写一个登录验证的程序,到新建一篇文章,修改一篇文章,增加一篇文章,实现分页,慢慢的,一个基本的网站就形成了,最终也就是现在这个样子。现在这个网站,如果对于一个熟手来说,不到一天就可以完成了,可是我是一个新手,所以只好一步一步来。接下来还有很多工作要做,功能上,如评论功能,RSS等还没有完成。而后台设计上,现在很多地方的HTML输出,还是用echo,没有做到MVC,接下来的机会是先用MVC重构整个网站,之后再添加评论和RSS等功能。
如果想看看现在网站的样子,可移步http://euler.dengshilong.org/
在用epoll提供telnet服务中说到,使用epoll提供telnet服务,用telnet登录后,然后输入命令,一切都很正常。只是由于一些原因,只能用nc命令,如echo stats | nc 192.168.168.128 9999
,这个命令如果在这台机器上运行,还是会返回正确的结果,然而当这个命令在其它机器上运行时,则有时候会返回结果,有时又不返回。于是用样的命令去测试memcache,不论是在本机上还是其它机器上,都可以返回结果。暂时想不到memcache是如何做到的,于是先用echo stats | nc -i 1 192.168.168.128 9999
来应付这个问题。可是这还不是最佳的解决办法,既然memcache能够做到,那么一定有解决的办法。
可喜的是,memcache是开源的,可以去看看它是如何解决这个问题的。打开memcached.c,文件不是一般的长,竟然有5000多行,慢慢找才知道memcache使用libevent,于是去了解libevent相关的知识,按照libevent官网和自带的示例程序,自己写了一个小例子运行,发现还是存在相同的问题。于是自己写了一个非常简单的单连接程序来取代epoll那一部分,可是还是存在相同的问题。问过同学之后,说是通过打印日志和tcpdump来查看网络包就一定可以找到。自己之前也用tcpdump查看过,客户端的确是有发数据的,服务端也收到了,可是为什么不显示收到的命令呢?依然没有解决我的问题,看来只有去《UNIX网络编程》中寻找问题的解答。
说实话,还真没有认认真真的看过《UNIX网络编程》这本书,因为之前对于网络编程总觉得异常繁琐,要调用socket,bind,listen,accept等函数才能建立一个链接,还要去填写sockaddr_in这个结构体,可是现在是工作,而且c就是这么底层,而网络编程也是很底层的,所以只好从头开始看起。此时对于TCP状态转换图才逐渐有些了解,没有想到啊,毕业5个多月后才知道这些,似乎有些晚了。难怪当初面试腾讯时,会被人鄙视说,许多工作的人都还能记得住这些转换图,你一个在校生怎么会不知道这些呢?其实当时很想和他说,这些都是工程实践很强的东西,没经常在这方面打滚,肯定不知道的,工作的人经常要接触这方面的东西,自然会记得住了。也罢,至少现在知道还不算晚。
看过书后,才知道有shutdown这样的东西,虽然以前看《UNIX环境编程》时,也看到过shutdown这个函数,可是没有写过实践代码,看过之后就忘记了。shudown的函数原型如下
#include int shutdown(int sockfd, int hoot);
返回:0--成功, -1--失败
该函数的行为依赖于howto参数的值。其中SHUT_WR选项,这个的意思是关闭连接写的这一半,当前留在套接口发送缓冲区的数据将被发送掉,后跟TCP的正常连接终止序列。用上这个,还是不行。
继续往下看,才知道有SO_LINGER这个选项,它使用linger结构体,如下:
struct linger { int l_onoff; /* 0 = off, nozero = on <em>/ int l_linger; /</em> linger time */ }lin;
当l_onoff是1,且l_linger是0时,close某个连接时,TCP将会丢弃保留在套接口发送缓冲区的任何数据并发送一个RST给对端。于是我回头看代码,建立连接这一段,我用的是公司封装过的函数,发现代码中将l_onoff设置为1,l_linger设置为0,于是我认为是找到了解决的办法,将l_onoff改为0就可以了。试过之后,发现客户端还是收不到数据,看来还存在其它问题。
书上有一个poll函数的例子,于是认真看看。先胡扯一下,网上都说,当epoll工作在水平触发模式时,则是一个高效的poll,可是他们都不给出一个epoll使用水平触发的例子,只是在那里将epoll工作在边沿触发模式的程序抄来抄去的,非常令人失望。而《UNIX环境编程》也只是给出poll的介绍,没有给出例子,看了之后还是很无厘头。还好俊杰同学留下这本书,真得感谢他。用上这个例子,测试之后,发现客户端是可以收到数据的,将这段代码去代替epoll那部分代码,发现依然可以收到,于是问题的范围就缩小了。
打印日志,终于找到问题的所在。首先说明一点,在边沿触发模式下,程序一直在while循环中read客户端发过来的命令,当返回-1,且errno设置为EAGAIN;而当读到结束符,也就是客户端发送FIN包过来时,read返回0。看代码中的处理,原来在代码中,是检查最后read的结果,而不是检查最后读到的字节总数,如果返回0的话,则关闭socket,不再去处理命令,那这里有什么问题呢?通过打印日志,发现如果将命令在与服务端同一台机器上执行,则会输出
n 6 nread -1
n 0 read 0
因为是在同一台机器上,所以两个包是单独处理的,这样就会给客户端返回命令后再关闭连接。而如果不是在同一台机器上,则输出就会发生变化。有时它会收到
n 6 read 0
此时已经从客户端读到命令的,同时也读到了结束符,在我的程序中就会直接关闭连接,这样就不会去处理命令,发送数据给客户端。而有时它会收到
n 6 nread -1
n 0 read 0
这时它就可以正常处理。这就可以解释为什么之前客户端有时可以收到反射,有时又收不到。而当在nc命令中加上-i 1后,客户端的FIN包将会在命令发送大约一秒钟后再发送,服务端就可以正常处理。
通过解决这个问题,了解许多网络的知识,受益良多。如果对于网络的知识不是很了解,调试的过程总不能得心应手,找不到合适的地方输出日志。如果当初能想到输出n和nread的值,也许就可以很快的找到问题的症结,只是没有想到这里。《UNIX网络编程》果然是本好书,在实践过程中来看这本书,才发现其中闪烁的真知见灼,都是来自于实践,有些怀念Richard Stevens了。另外tcpdump确实是个好东西,对于查看网络包,非常方便。
最终的代码见这里用epoll提供telnet服务的代码续
记得一年前找工作时,阿里巴巴的面试官问了这么一道题,汉字在C语言中是怎么存储的,或者说C语言是如何处理汉字的,对于那时的我是一脸茫然,因为实战经验太少了。前不久,在处理中文标题时出现了乱码问题,组长只在一段代码中改了一句话就解决了问题,觉得非常神奇,于是看这段代码,原来就是关于编码处理的,于是认真的看了这段代码。
通过努力,算是明白了这段代码,其实也就是用了iconv函数族来进行编码之间的转化,再进行特殊处理。例如如果最终目标语言是GBK,可能还需要进行全角和半角之间的转化,繁体到简体的转换。事实上,我们处理时出现了乱码问题正是在繁体到简体的转换时写错了,导致出现乱码。
这里只想写写我自己的理解,在我看来,任何编码保存的文件最后都是字节流,在读取这些字节流时,你只有采用保存文件时采用的编码才不会出现乱码。这就好像加密与解密一样,例如如果一个文件是用utf-8编码的,但你是用GBK打开,此时就会出现乱码了,因为两种编码的编码方式不同。当然还是有一些例外情况,例如如果你是用GB2312保存的文件,用GBK打开是不会出现乱码,因为GBK是兼容GB2312的,GB2312的所有编码的值在GBK中是一样的,只是GBK还利用了GB2312没有用的一些空间来增加了一些编码。而GB18030则又扩充了GBK,包含了更多的字符。
在C语言中,用字符串来保存文本,这些文本都是字节流,只是每种编码占用的空间不同。例如“我是中国人”这5个字,如果是utf-8编码的,因为每个汉字占用3个字节,则要占用15个字节,用strlen可以测出来。如果是GBK编码的,因为每个汉字占用2个字节,则要占用10个字节。对于这样的回答,不知道那个面试官会不会满意呢?
所以这里有一个问题是,给你一个字符串,如果没告诉你用什么编码的,那么要怎么判断它是什么编码的。
最近实现一个小功能,统计引擎中搜索次数,成功的次数,失败的次数,增加的记录等等,并通过socket连接提供服务,就像memcache一样,可以通过命令查询当前的状态。
于是想到以前写FTP服务器时与这个功能很类似,当时是用的多进程,可是现在不同,只能用线程,于是想到用多线程,来一个连接就开一个线程去处理,可是组长说这还是太麻烦了,可以看看select,poll,epoll等等,说实话,虽然这些都有听过,但都没有用过,这次正好可以试试。看了《UNIX环境高级编程》上的例子,大概知道什么意思,可是书上又没有例子,而且对epoll都没有介绍,只好参考网上的资料,在Epoll在LT和ET模式下的读写方式中有比较详细的介绍,依葫芦画瓢,写了一个,很快就实现了,用telnet做客户端,能够提供服务。
只是还有一些细节需要处理,如果epoll_wait是是否设置timeout,客户端关闭连接怎么处理等等,在另一篇文章中说到当read时,返回0时,就代表客户端关闭连接了,试了一下,可行。 这里本来是用发牢骚的地方,所以不想把代码贴这里,如果想看具体代码,请移步用epoll提供telnet服务的代码
参考资料
在《空闲时玩的游戏manufactoria》,《通关manufactoria》说过在manufactoria上花了很多时间,并借助外力的帮助下通关了,但是一直没有将它记录下来,现在记下通关方法,以免以后又忘记了。这个游戏对于我对程序设计的理解有很大帮助,给了我许多兴趣。本来给出解答是不应该的,这样会剥夺了别人思考的权力,就像以前的《吉米多维奇》习题集,本来非常火爆的,结果有好事者搞出了习题解答,之后这套习题集就冷下来了。可是每个人都有局限性的,看过《三体》之后,让我知道技术爆炸这个词,也就是徒弟本来不会一项技术,师傅教他之后,他比师傅做的还好,并有了创新。秉着这个观点,把解答贴在这里,让更多的人体验技术爆炸。
关卡按照从左到右,从上到下计数。
问题描述:设计一个只有五个状态(外加一个终止状态)和两个字符集的图灵机,要求能够停机,并且状态转移后要求向左走或向右走,使图灵机走的步数尽可能的多。在这里看到busy beaver问题,看到博主用遗传算法,于是也想自己动手写程序,生成一个图灵机,因为之前没有学过遗传算法,于是上网找了一下资料,找到一篇介绍遗传算法的很好例子,遗传算法:内存中的进化 .写的挺久的,因为老是出现错误。
跑了三个多小时,得到一些结果,361,1004步。用Python写还是挺好的,就是程序运行的慢了一点。遗传算法很有意思,简单的规则,经过不断演化,能解决问题。
上一次说被Python的缩进弄的头痛,现在终于知道原因了。原来只要将Tab设置为4个空格宽度,并且将Tab改为空格就可以了。之前说要写一个博客系统放在SAE上,现在想想还是放弃了,因为意义不大,现在这个博客挺好的,界面很清爽。
最近一段时间,不知道要做什么了,但总不能闲着,于是学习Python。之前也接触过Python,但真正用Python写程序,还是最近的事。
以前一直想写一个小工具,用来统计自己写过的C/C++程序的行数,但是一想到要自己去看VC++的目录操作,就放弃了。最近学了Python之后,很容易的就写了一个,只有十行左右。源程序放在这里源程序行数统计工具,此前还写了一个人人网日志下载工具,输入人人网用户名和密码就可以使用。使用正则表达式去提取链接,因为人人网的页面变动后,程序现在不能用了。
现在尝试用Tornado写一个个人博客系统,然后放到SAE中去,现在还在写,以及有雏形了。感觉写Python还是很舒服的,但是缩进问题有时挺麻烦的,因为习惯了用Tab键缩进,而很多人的程序都是用四个空格,看来还是自己的习惯不好,得改。
发现Python真是一门不错的语言,写起来方便,不会令自己纠结于细节,调试很方便,因为有错误跟踪,不像以前写C程序时,一个指针问题就要调很久。