月度归档:2011年03月

用Python写一个本地Sogou代理服务器程序

真难以自信,使用Python强大的类库,可以用这么简短的代码就写好一个本地的HTTP代理!
真难以自信,这是我用Python花了连续6个小时写的第一个程序(之前写的一个Hello world除外)!
真难以自信,我就这样爱上了Python……
所以,以后在我博客上估计会有不少Python代码了……

现在我使用Python仍然停留在Thinking In The C++ Way而不是Thinking In The Python Way,所以最近会打算看一些Python类库里面的代码,看看别人是如何写的。

一开始使用Python3.2编写这个代理服务器,后来移植到Python2.7上去,这两个版本的差异还真的挺大的,我记得移植的时候改了不少地方。
我用py2exe编译了不需要Python运行环境的Windows可执行文件版本,方便大家测试,貌似编译之后比较臃肿。

这里有一篇关于如何使用py2exe编译python脚本的文章:
www.cnblogs.com/jans2002/archive/2006/09/30/519393.html

下载地址:sogouproxy win32

Python源代码:
继续阅读

用C++写邮件服务器,用上DKIM,可以给陌生人发正常邮件了

研究完SMTP协议,也涉猎了不少RFC文档,粗粗略略了解到不少东西。一开始在自己电脑用telnet给对方直接发送电子邮件,只要发送方的邮箱地址不在对方的联系人中,都被直接视为垃圾邮件了。后来给在服务器上测试,给邮箱域名增加了SPF记录,但是效果不是很明显,还是大部分被视作垃圾邮件了。具体操作可以参见百度百科:SPF记录

奇怪的是,我发现不少网站的邮箱服务器也只有SPF记录,甚至有的没有设置SPF记录(例如dot.tk),都可以正常发送到我邮箱里。或许我的IP段不好吧。据说,支持DKIM数字证书的邮箱服务器更能有效地防止垃圾邮件,所以我决定让我的邮箱服务器也支持DKIM的签名。

我在服务器端使用GPL发布的PDKIM库实现的数字证书签名功能。说到数字证书,就想到RSA了,又想到Public Key和Private Key。这个Public Key是存放在DNS上某域名的TXT记录里,跟SPF记录的做法差不多。Private Key是邮箱服务器发信的时候,用来对邮件内容产生一个Hash字段的。

例如,我使用PDKIM产生的是:

DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=mail.xiaoxia.org; 
    s=s1;h=Content-Transfer-Encoding:Content-Type:To:From:Subject; 
    h=qc1pcFZEKobZ5/u35TeoRGObVNBgyRTlkjjKiya6CZk=;
    b=o4a6wMV6C8Me9TJMvv7gb233or0Qam+R8dhwDkXUgArTGcbS0El8YyYM527MdJpQSnlT9QicPi
    MSKQOY8GEcCKC2J9WIHnpxlMfqv+e+7N+HnMUyBjYTdtu8Yu1LD0CQhf1iDywBmYquwue5ON+7hp1taF7lLnfsNwyyramzYrE=;

今天测试的时候,一直遇到问题,在PDKIM库中出现异常。今晚才发现问题所在,原来是我末尾少了一个”\r\n”导致的。给Gmail发送了一封邮件,显示DKIM数字签名已通过!!!


继续阅读

SMTP简单邮件传输协议 – 用telnet发送电子邮件

以前对互联网感兴趣的时候,很早就接触HTTP和FTP协议而一直没有关注SMTP协议是因为在这方面应用的需求比较少。
我的博客使用SMTP来给评论者发送邮件通知,但使用的是邮件中转服务器,需要验证账号之后提交给中转服务器发送。在PHP中使用此种方法发送邮件的实例可以参见:博客留言短信通知

下面用telnet实践另外一种方法,直接连接域名MX记录的地址,无须账号直接投递邮件。
假如现在要投递一封邮件到gmail.com去,如何获得gmail.com的MX记录呢?

最简单的方法是使用系统自带的nslookup等工具去查询DNS的信息。例如:

Z:\py>nslookup
默认服务器: ns3.gd.cnmobile.net
Address: 221.179.38.7

> set type=mx
> gmail.com
服务器: ns3.gd.cnmobile.net
Address: 221.179.38.7

非权威应答:
gmail.com MX preference = 10, mail exchanger = alt1.gmail-smtp-in.l.google.com
gmail.com MX preference = 20, mail exchanger = alt2.gmail-smtp-in.l.google.com
gmail.com MX preference = 30, mail exchanger = alt3.gmail-smtp-in.l.google.com
gmail.com MX preference = 40, mail exchanger = alt4.gmail-smtp-in.l.google.com
gmail.com MX preference = 5, mail exchanger = gmail-smtp-in.l.google.com

gmail.com nameserver = ns3.google.com
gmail.com nameserver = ns2.google.com
gmail.com nameserver = ns4.google.com
gmail.com nameserver = ns1.google.com
ns1.google.com internet address = 216.239.32.10
ns2.google.com internet address = 216.239.34.10
ns3.google.com internet address = 216.239.36.10
ns4.google.com internet address = 216.239.38.10

继续阅读

C/C++多线程编程介绍(1) – QuickSort

某同学写了一个多线程快排,出现了莫名其妙的问题!
今晚自己也花点时间写一个类似的多线程排序代码。对于QuickSort!一直认为,出自不同人的手,在写法上至少那么一点儿的差别。
下面是我最常用的单线程版本的写法,

int quick(int A[], int a, int b)
{
	int l = a, r = b, m = A[(a+b)>>1];
	do{
		while(A[l]<m) ++l;
		while(A[r]>m) --r;
		if(l<=r) swap(A[l++], A[r--]);
	}while(l<r);
	if(a<r) quick(A, a, r);
	if(l<b) quick(A, l, b);
}

我在Linux下使用pthread线程函数库实现多线程的排序。下面是用到的一些多线程相关的结构和函数。
继续阅读

郁闷!研究了一下Sogou的代理服务器验证协议

昨天晚上收到一条来自13800138000的信息,说过手机卡没钱了,不足扣除月租,然后就停机了!这可是令我大吃一惊的,前几天才充够了85元等着扣月租,今天一查卡上余额只有25元,几天之内我可怜的60元飞到哪里去了?上网查了一下清单,发现CMCC的WLAN因为超时上网,额外抽了我50多元……本来这个套餐才20元。另外还有个啥移动梦网抽的10元就不说了……我郁闷啊!用超时了也不给我一个提醒,还帮我正常登录,正常使用……真想骂人,又骂不出来。移动多我一个不多,少我一个不少,唉!
废话说太多了,不过说出来我心里也舒服了-_-! 没这事就不会无聊到去支持Sogou浏览器……

下面进入正题:
打开Sogou浏览器,用教育网访问外部网的时候,直接是连不上的,必然要经过Sogou的代理服务器中转。如何获取代理服务器列表,如何判断网络类型等不是我想要讨论的内容。下面仅说说它如何验证客户端身份

GET http://xiaoxia.org/ HTTP/1.1
Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
Accept-Encoding: gzip,deflate
Accept-Language: zh-CN,zh;q=0.8
Cache-Control: max-age=0
Connection: keep-alive
Host: xiaoxia.org
Proxy-Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.33 Safari/534.3 SE 2.X MetaSr 1.0
X-Sogou-Auth: 9CD285F1E7ADB0BD403C22AD1D545F4A/30/853edc6d49ba4e27
X-Sogou-Tag: f19f1ae7
X-Sogou-Timestamp: 4d7858f6

有些细心的人看了上面的HTTP内容,会发现一个奇怪的问题。好吧!我100%肯定上面这个HTTP头是搜狗浏览器发出去的,我只是用了Chrome的浏览器皮肤,但是没想到它的User-Agent都换了Chrome的……

一般来说,用于搜狗服务器验证的只有3个字段,X-Sogou-*。大致了解一下他们的意思,从名字上就可以看出来了。
继续阅读

用C++和libSDL开发游戏系列(3) – 简单A*算法寻径

游戏中经常会用到寻径算法,最简单的寻径算法就是走直线或者总是往最接近目的地的方向走,遇到障碍物就不可以继续前进。这样虽然得到的路线一定是最短的,但是一旦碰到障碍物就被卡住了,显然不够灵活。BFS、A*是最常见的路径寻找算法。BFS算法从起始点一直向周围辐射扩张盲目地寻找目的地,虽然找到的路径一定是最短路,但是效率不高。A*算法加入了对每个发现了的位置的评估,选择与目的地接近的方向搜索,比较灵活,效率也很高。

A*算法是改进的BFS(宽度优先搜索)算法,区别在于A*算法在搜索下一个点的时候,会在等待搜索的点中寻找一个最优的。这里最优的意味着,最容易到达目的地。最简单的一个评估方法是,看当前位置到目的地还有多远。

设f(x, y)为对位置(x, y)的评估函数,g(x, y)为从起始点到当前点所经过的路程,h(x, y)为从当前点到目的地还需要的大概路程。则f的表达式可以为f(x, y) = g(x, y) + h(x, y)

以下面这个地图为例,比较BFS和A*的差异。

19 33
 ############################### 
 #                       #     # 
 #                       #     # 
 #                       #  @  # 
 #                       #     # 
 #                       #     # 
 #     #     #######     ### ### 
 #     #     #           #     # 
 #     #     #           #     # 
 #     #     #        x  #     # 
 #     #     #           #     # 
 #     #     #           #     # 
 #     #     #######     #     # 
 #                             # 
 #                             # 
 #                             # 
 #                             # 
 #                             # 
 ############################### 

地图中,#表示障碍物,x表示起始位置,@表示终点,.表示搜索过的位置,*表示最终找到的路径。
假设地图上的怪物只可以上下左右四个方向移动。
继续阅读

用C++和libSDL开发游戏系列(2) – 使用SDL_mixer播放音乐和音效

SDL_mixer的用法很简单,它可以帮助你在你使用了SDL的项目中播放音频文件,包括
项目网址是:http://www.libsdl.org/projects/SDL_mixer/

这里有SDL_mixer的API说明,很详细,而且用法很简单,看了代码你就知道了!
http://www.libsdl.org/projects/SDL_mixer/docs/SDL_mixer_frame.html

编译选项:
g++ -lmingw32 -lSDLmain SDL_mixer.dll -lSDL main.cpp

随手写的一坨代码:

#include <stdio.h>
#include <SDL/SDL.h>
#include <SDL/SDL_mixer.h>

int main(int argc, char **argv)
{
	/* 初始化SDL */
	SDL_Init(SDL_INIT_AUDIO);
	/* 在使用SDL_mixer前进行初始化 */
	int ret = Mix_Init(MIX_INIT_MP3);
	if(ret & MIX_INIT_MP3 != MIX_INIT_MP3){
		perror("failed to initialize mp3 module.");
		exit(1);
	}
	
	/* 设置音频格式,使用44100的cd格式即可 */
	if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024)==-1) {
		fprintf(stderr, "Mix_OpenAudio: %s\n", Mix_GetError());
		exit(2);
	}

	/* 加载一个wav音频文件 */
	Mix_Chunk* wav = Mix_LoadWAV("./heal.wav");
	if(wav == NULL){
		fprintf(stderr, "Mix_LoadWAV: %s\n", Mix_GetError());
		exit(1);
	}
	/* 播放音频文件,第三个参数是循环次数这里设置为0表示播放一次,1表示播放两次,-1表示无穷次 
	 * 第一个参数是音频轨道,轨道数目可以通过Mix_AllocateChannels函数来改变,-1表示随机选择一个空闲的轨道 */
	Mix_PlayChannel(-1, wav, 0);
	
	/* 加载一首MP3吧! */
	Mix_Music* mp3 = Mix_LoadMUS("./music.mp3");
	if(mp3 == NULL){
		fprintf(stderr, "Mix_LoadMUS: %s\n", Mix_GetError());
		exit(1);
	}
	/* 播放一次 */
	Mix_PlayMusic(mp3, 1);
	/* 等待播放结束 */
	while(Mix_PlayingMusic())
		_sleep(1000);
		
	/* 资源回收处理 */
	Mix_CloseAudio();
	Mix_Quit();	
	return 0;
}

我对P2P通俗易懂的理解

Tomb Raider游戏出了局域网版本之后我就开始研发互联网版本的联机游戏。之所以局域网版本的不能直接用在互联网上是因为未有解决局域网内主机与外部网络或者与其他局域网的内部主机之间的通信问题。按照一般的P2P程序开发流程,需要一个拥有公网IP的服务器使用UDP Hole Punching技术可以达到把两个不同局域网的主机连接起来的目的,前提是局域网必须是属于圆锥型网络(Cone)而不是对称型的(Symmatric)。

圆锥和对称有何差别呢?

这里举个简单的例子。当你在局域网内向一台服务器A发送一个数据包时,网络地址转换器(NAT)会产生一个会话,并使用一个公网IP的端口与那台服务器通信,当那台服务器返回数据的时候,NAT会根据会话里的对应关系,把数据投递到局域网内你那台主机上。如果找不到对应关系,NAT是会把数据包丢弃的!通常还可能会收到10052的错误消息(Network dropped connection on reset.)这时候有一个这样的问题,当你的在局域网的那台主机使用同一个端口向另外一台服务器B(IP或端口不同)发送数据的时候,NAT创建一个新的对应关系时是否仍然会使用上次你跟服务器A通信时用的端口?如果会,那么它是圆锥型的,不会就是对称型的。

就像下图一样,


继续阅读