20121228

用邮件分割和传送大文件,python实现 III

用邮件分割和传送大文件,python实现 III

4. 接收端baoyu

4.1 导言和import

这部分,是从zhumao.py中抄过来的。

1 #! /usr/bin/python
2 # -*- coding: utf-8 -*-
3
4 # baoyu, 邮件接收者
5 __usage__ = "usage: %baoyu.py [--help]"
6 __version__ = "baoyu by Young 2012-12-21"
7
8 from optparse import OptionParser
9 import base64
10 import time
11 import sys
12 import poplib
13 import time
14 from email.Parser import Parser
15 from email.header import decode_header
16

4.2 helper函数们

这里是一些在后面的业务逻辑实现中要调用的一些函数。它们在此声明和定义,定义的方式一般应依据动机、目的,而不是根据实现手段。也就是说,使用的词汇应该是分析阶段的词汇。函数被从"后面的业务逻辑"中抽取到这里,可能因为被调用很多次,也可能只调用一次,但是在业务上具有较为鲜明的特征。如果调用很多次,抽取出来的原因之一就是重用,这样可以避免后续维护的时候一旦有修改需求,这个功能相关的很多地方都要修改;如果只调用一次,但是业务特征明显,就是为了信息隐藏,以后或别人读代码的时候要容易一些。

根据场合和组织方式的差异,这些函数有不同的称呼。在C++/java中,私有函数基本上实现了这样的功能;在flex/bison及我一时没想起来的很多领域中,它们被称为helper函数。

17 ######
18 # helpers
19 ######
20 def getheader(header_text, default="ascii"):
21 """Decode the specified header"""
22 try:
23 headers = decode_header(header_text)
24 header_sections = [unicode(text, charset or default)
25 for text, charset in headers]
26 return u"".join(header_sections)
27 except:
28 return u"".join("invalidated encode")
29

上面的代码是从某位貌似日本人的站点上抄来的,它的名字其实不应该叫做getheader,而是"根据charset解码"邮件头或正文。

30 def get_msg(which):
31 return "\n".join(server.top(which, 1)[1])

这是根据 which指定的邮件id号,从邮件服务器上取得对应的邮件,但是不删除。server.top()。详见手册[http://docs.python.org/2/library/poplib.html],"POP3.top(which,
howmuch)"。

33 def get_from(msg):
34 email = parser.parsestr(msg)
35 return email.get("From")

上述的 pserser 就是 第14行 "from email.Parser import Parser"
中的Parser的实例,在下面的第82行。手册[http://docs.python.org/2/library/email.parser.html]。这个parser是专门用来从邮件中析出邮件头部等各个部分的。

第35行,解析出From部分,也就是邮件发送者地址。baoyu.py根据这个来判断某封邮件符合"zhumao-baoyu"协议的发送者部分。

这个粗糙的协议包括:发送者、邮件主题、第几封邮件、共几封。其中,发送者和邮件主题作为下载和删除邮件的过滤条件。

37 def get_body(which):
38 msg = "\n".join(server.retr(which)[1])
39 email = parser.parsestr(msg)
40 return email.get("BODY_START")

与第33行开始的函数类似,get_body用于取得which指定的id对应的邮件的body。不同的是,get_body把这封邮件标记为已读。

此外,上文提到,我们为了解析方便,在body的开头标记了"BODY_START:",所以此处,body可以视为"BODY_START"部分的值。

42 def get_subject(msg):
43 return getheader(parser.parsestr(msg).get("Subject"))

还是与第33行开始的函数类似,取主题部分。

45 # e.g. [base64-2] 1/2
46 def get_cur_num(subject, subject_prefix):
47 start = subject.find(opt.subject_prefix)+len(opt.subject_prefix)
48 slash = subject.find('/', start)
49 return int(subject[start:slash])

51 def get_all_num(subject, subject_prefix):
52 start = subject.find(opt.subject_prefix)+len(opt.subject_prefix)
53 slash = subject.find('/', start)
54 return int(subject[slash+1:])

以上两个函数,是从邮件头里取得这是第几个包、一共几个包这两个数据,使用的方法是字符串处理,以"/"分割类似"[base64-2] 1/2"的邮件头部。

4.3 命令行解析

与发送端zhumao.py的命令行解析类似,解释从略。

57 #--------------------------------------------------------------------
58 # 命令行解析
59 p = OptionParser(usage=__usage__, version=__version__, description=__doc__)
60 p.add_option("-F", "--file", dest="filename",
61 help="file need to be saved", metavar="FILE",
62 default="test.out")
63 p.add_option("-P", "--pop", dest="pop",
64 help="pop3 server", metavar="POP3_SERVER",
65 default="pop3.nenu.edu.cn")
66 p.add_option("-u", "--user", dest="user",
67 help="user name", metavar="USER")
68 p.add_option("-p", "--password", dest="password",
69 help="password", metavar="PASSWORD")
70 p.add_option("-f", "--from", dest="fromaddr",
71 help="from whom to be filtered", metavar="FROM",
72 default='gift.young.1@gmail.com' )
73 p.add_option("-L", "--subject", dest="subject_prefix",
74 help="the prefix of mail subject as filter",
metavar="subject_prefix",
75 default='[zhumao_baoyu_mail]')
76
77 (opt, args) = p.parse_args()
78

4.4 准备连接邮件服务器

82 parser = Parser()
83 server = poplib.POP3(opt.pop)
84 server.user(opt.user)
85 server.pass_(opt.password)
86 server.set_debuglevel(0)
87 sleep = 0.1
88 count = server.stat()[0]
89 d = dict()

使用pop3接收,各种参数设置。

第88行,取得stat命令时的邮件数量,即未读邮件数量。

第89行,初始化一个字典 (映射)。后面准备把第1封邮件放到"1"的值里面,第2封邮件放到"2"的值里面,依此类推。这样,在拼邮件的时候,可以按key排序。

4.5 接收邮件

检查邮件,根据邮件头过滤,下载并删除符合要求的邮件,显示接收百分比。

从第90行到第107行,是一个循环,用于遍历所有邮件,并在第103行当查找到所有符合要求的邮件 (数量达到要求)以后跳出循环。

90 for i in xrange(1, count, 1):
91 current = get_msg(i)

调用helper函数 get_msg,取得id为i的邮件 (的邮件头和body第一行) 。

92 if (get_from(current).find(opt.fromaddr) != -1 and
93 get_subject(current).find(opt.subject_prefix) != -1):
94 b = get_body(i)

如果邮件的发送者符合要求 (比如 zhumao@nenu.edu.cn) ,并且主题的prefix也符合要求 (比如 [base64-2]),取整封邮件。

如果不过滤直接在遍历的时候取整封邮件,因为邮件数量众多,带附件的邮件又很大,性能会比较低。而仅遍历邮件头,对网络带宽就没有那么高的要求了。

95 print '+-------------------------'
96 print '|' + str(i)+' '+ get_subject(current)
97 # print '|' + b
98 print '+-------------------------'

显示进度,避免用户着急。

99 current_seq = get_cur_num(get_subject(current), opt.subject_prefix)
100 all_number = get_all_num(get_subject(current),
opt.subject_prefix)

调用helper函数,取得当前是这一文件的第几包邮件 和 一共几包邮件。一共几句邮件,数值在遍历中一直不会变,但是重复计算了很多次。

101 print (str(current_seq) +'/'+ str(all_number))

再显示进度,第几封,共几封。

102 d [current_seq] = b

把第N封邮件放到字典键N的位置。参见前面提到的第89行,这里是向字典中插入数据。

103 if len(d) >= all_number :
104 break
106 else:
107 print 'filtered out: '+str(i)+'/'+str(count)+'
'+get_subject(current)

如果把这个文件对应的所有邮件都已下载完毕,跳出循环;否则的话,也告诉用户一声,这封邮件不符合条件,不然出现大量不符合条件的邮件时,用户看到的是程序假死。

109 server.quit()

收完邮件,关闭连接,保持优雅。

4.6 合并文件

111 # 合并文件
112 s=""
113 for k in xrange(1, all_number+1, 1):
114 s += d[k]
115

接key的顺序遍历字典d,把对应的值拼接在一起。这个对应的值来自第102行的插入,此处是取出数据。字典d综上所述,在第89行初始化,在第102行插入数据,在第114行读取使用。如果不用字典,而使用一个链表,以下标代替key,也可以。

4.7 解码文件

我们拼接出来的文件是base64编码的,所以需要解码,然后写出。

116 # 解码文件
117 s = base64.standard_b64decode(s)
118
119 file = open (opt.filename, "w")
120 file.write(s)


5. 回顾数据流

发送端的数据流为: 文件binary -> 读入文件 -> base64编码 -> cut -> smtp.

接收端的数据流为:pop3 -> merge -> base64解码 -> 写出文件 -> 文件binary。

如果思想的传递,也能无误地这样传输,该有多好。

6. 进一步的工作

还有很多进一步的工作可以做,让这个协议及应用程序更好一些。比如,加错误检校,指定重传错误的包而不是全部重传,由baoyu访问zhumao的共享目录并指定下载某个文件,不使用SMTP/POP3而是通过论坛的贴子传递信息。

还可以在传送前加压缩 (不过对于视频和音频,没啥意义)。

还可以用这种隧道做成反向代理……

未来有无数种可能。可能这正是人生的迷人之处,如果今天就能看到明天及所有以后的日子,那还有什么希望可言。

完整代码在这里 [http://my.csdn.net/my/code/detail/33574] 和
[http://my.csdn.net/my/code/detail/33573].

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

博客会手工同步到以下地址:

[http://giftdotyoung.blogspot.com]

[http://blog.csdn.net/younggift]

20121227

用邮件分割和传送大文件,python实现 II

用邮件分割和传送大文件,python实现 II

3. 软件的使用过程

下面这段,是软件写完以后运行的效果,不过,在软件开始写以前,它的样子就已经在我的心中。用个去年还是前年流行然后就消声匿迹的词来形容,软件在写第一行代码以前,就应该有个"愿景"。


以下,以发送191字节的 test.in
为例,分成3个包,每包100字节。从young@nenu.edu.cn发出,发给young@nenu.edu.cn。每包100字节是参数的默认值指定的。

3.1. 发送方zhumao

~/running/zhumao-baoyu-mail $ python zhumao.py -f young@nenu.edu.cn -t
young@nenu.edu.cn -L "[test-in]" -p "mypass***" -F test.in

To:young@nenu.edu.cn From: young@nenu.edu.cn Subject:[test-in] 1/3

sent.

To:young@nenu.edu.cn From: young@nenu.edu.cn Subject:[test-in] 2/3

sent.

To:young@nenu.edu.cn From: young@nenu.edu.cn Subject:[test-in] 3/3

sent.

3.2 接收方baoyu

~/running/zhumao-baoyu-mail $ python baoyu.py -u young@nenu.edu.cn -p
"mypass***" -L "[test-in]" -f young@nenu.edu.cn -F test.out
+------------------------- |1 [test-in] 3/3 +-------------------------
3/3 +------------------------- |2 [test-in] 2/3
+------------------------- 2/3 +------------------------- |3 [test-in]
1/3 +------------------------- 1/3

以上接收过程可以看到,一共3包,第3包的接收早于第2包,早于第1包。

用diff对比发送和接收到的文件,一致。

~/running/zhumao-baoyu-mail $ diff test.in test.out
~/running/zhumao-baoyu-mail $


3.3. 手册,帮助

$ python zhumao.py --help Usage: %zhumao.py [--help]

Options: --version show program's version number and exit -h, --help
show this help message and exit -F FILE, --file=FILE file need to be
sent -S SMTP_SERVER, --smtp=SMTP_SERVER smtp server -f FROM,
--from=FROM mail sender -p PASSWORD, --password=PASSWORD password -t
TO, --to=TO mail receiver -s SIZE, --size=SIZE size of each mail -L
subject_prefix, --subject=subject_prefix the prefix of mail subject

$ python baoyu.py --help Usage: %baoyu.py [--help]

Options: --version show program's version number and exit -h, --help
show this help message and exit -F FILE, --file=FILE file need to be
saved -P POP3_SERVER, --pop=POP3_SERVER pop3 server -u USER,
--user=USER user name -p PASSWORD, --password=PASSWORD password -f
FROM, --from=FROM from whom to be filtered -L subject_prefix,
--subject=subject_prefix the prefix of mail subject as filter

4. 发送方zhumao

以下是发送方zhumao.py的代码及讨论。希望能为像我一样的初学者带来启发的同时,渴望熟悉python什么的大牛们指导和批评我代码的各种错误,一方面我求进步,另一方面也免得我误导别人。谢谢啦。

这两天又访问不了github了,所以暂时没传上去。各位将就着看吧。

不连续的行号,是空行,我解释的时候略过了。

4.1 基本相当于导言

1 #! /usr/bin/python 2 # -*- coding: utf-8 -*- 3 4 # zhumao,邮件发送者

第1行,注释,指定脚本解释器。这和shell程序设计是一个路子。

第2行,注释,文件编码。祖国尚未统一世界,看来各种编码的乱像还会继续相当长时间。

5 __usage__ = "usage: %zhumao.py [--help]" 6 __version__ = "zhumao by
Young 2012-12-21"

我照抄的,看效果是显示使用方法。这个实现有意思,方便程序员与用户沟通啊。增加一点方便,估计就多几个百分比的程序员实现这一功能。

8 from optparse import OptionParser 9 import smtplib 10 import base64
11 import time 12 import sys

我猜就是 C里的include,猜的。还有名字空间、模块这样的作用。from的作用,似乎是使得模块中的符号名在当前作用域可见。这些模块是后面的代码要用到的。

4.2 命令行解析

这里使用了

8 from optparse import OptionParser

中指定的模块,第15行中的p是个OptionParser实例。在第16行以后,调用实例的方法
p.add_option。这些参数,基本可以根据出现的顺序、内容,与上述"愿景"对比,猜出来含义。不赘述。

14 # 命令行解析15 p = OptionParser(usage=__usage__, version=__version__,
description=__doc__) 16 p.add_option("-F", "--file", dest="filename",
17 help="file need to be sent", metavar="FILE", default="test.in") 18
p.add_option("-S", "--smtp", dest="smtp", 19 help="smtp server",
metavar="SMTP_SERVER", default="smtp.nenu.edu.cn") 20 # help="smtp
server", metavar="SMTP_SERVER", default="smtp.gmail.com") 21
p.add_option("-f", "--from", dest="fromaddr", 22 help="mail sender",
metavar="FROM") 23 p.add_option("-p", "--password", dest="password",
24 help="password", metavar="PASSWORD") 25 p.add_option("-t", "--to",
dest="to", 26 help="mail receiver", metavar="TO") 27
p.add_option("-s", "--size", dest="size", 28 help="size of each mail",
metavar="SIZE", default=100) 29 p.add_option("-L", "--subject",
dest="subject_prefix", 30 help="the prefix of mail subject",
metavar="subject_prefix", default='[zhumao_baoyu_mail]') 31 32 (opt,
args) = p.parse_args()

到这里为止,命令行参数 (及其默认值)就已经成为p的成员了。后面这样引用,比如: opt.smtp。

第32行,这个返回值对C程序员来说,看起来有点奇特。手册[http://docs.python.org/2/library/optparse.html]说

"parse_args() returns two values:

options, an object containing values for all of your options—e.g. if
--file takes a single string argument, then options.file will be the
filename supplied by the user, or None if the user did not supply that
option args, the list of positional arguments leftover after parsing
options"

4.3 输入文件

34 # 输入文件35 file = open (opt.filename, "r") 36 s = file.read()

第35行,opt.filename是从命令行中解析出来的,在我们的"愿景"中,就是test.in。open,
read,这样的写法,容易望文生义,而且似乎也对,不赘述。执行完这段以后,文件的内容全在s中了。

4.4 编码文件

38 # 编码文件39 s = base64.standard_b64encode(s) 40 41 ## sys.stdout.write(s)

第39行,使用了 base64编码 字符串s,编码的结果再赋值给s。第41行是我写程序的时候用来输出测试的。

4.5 准备发送,登录

43 # 准备发送,登录44 sleep = 1 45 to = opt.to 46 user = opt.fromaddr 47 pwd
= opt.password 48 smtp = opt.smtp

第44行,准备每发送一包睡一秒。根据你用的SMTP服务器对你的坏人判断有多严格,你可以调整sleep的值。

从第45行至第48行,从命令行解析的结果中得到 发给谁、发送者、SMTP发送者的口令、SMTP服务器地址。

49 smtpserver = smtplib.SMTP(smtp) 50 # smtpserver =
smtplib.SMTP(smtp, 587) # for gmail 51 smtpserver.ehlo() 52
smtpserver.starttls() 53 smtpserver.ehlo 54 smtpserver.login(user,
pwd)

第49行使用smtplib,构造出smtpserver实例。如果用的是gmail,端口需要指定为587。

从第51行到第54行,根据你选的smtp服务器的要求决定对它说些啥。我的smtp服务器要求先
扩展的hello,然后tls架密,然后再问候一次,然后登录。如果不知道应该说些啥,可以用thunderbird登录一次,用sniffer看一下正确的步骤。

4.6 拆分-标记-发送

麻烦的地方到了。

4.6.1 拆成

56 # 折分文件57 end = len(s) 58 for i in xrange(0, end, opt.size): 59 if
i+opt.size < end : 60 current = s[i:i+opt.size] 61 else: 62 current =
s[i:]

python的for循环语法,我老是忘。希望这回能记住。

"for 变量 in 待遍历的东西 : "。

这里,待遍历的东西是一个数据范围,用xrange生成,从0开始,到end结束,步进为 opt.size。

在循环体中--这也是python有意思的一个地方,缩进是语法要求,而不仅是为了美观--如果没有到最后一包,当前的这包就是字符串切割出来的。s[i:i+opt.size]表示切割s,从
i 切到 i+opt.size;如果不是最后一包,从i切到结尾,写作 s[i:]。

python切割字符串的方法,还真是有C的风格,把字符串当成纯粹的容器/数组看待。

4.6.2 标记

还在循环里。

63 # 标记64 subject = opt.subject_prefix + ' '
+str(i/opt.size+1)+'/'+str(end/opt.size+1) 65 header = 'To:' + to +
'\n' + 'From: ' + user + '\n' + 'Subject:' + subject +' \n' 66 current
= 'BODY_START:' + current + '\n' + 'BODY_END:NOTHINGGOESHERE' 67 print
68 print header

第64行,生成主题subject,第65行,生成邮件头header,第66行,生成正文。第67和第68行,不是为了调试,而是为了避免用户在长时间切割的时候不知道跑了多远,还剩多远。

这里输出的东西就是下面这段:

To:young@nenu.edu.cn From: young@nenu.edu.cn Subject:[test-in] 2/3

sent.

其中,主题里的 [test-in]
是加的标记,包师弟接收的时候可以用这个标记过滤出我们特别的邮件。太阳底下无新鲜事,大毅同学提到在cisco设备中,vlan/trunk也使用了类似的方法做标记tag。后面的2/3表示共3包,这是第2包,用来在baoyu接收端拼接文件的时候作为顺序的依据,也用来显示出来,避免包师弟了解下载了多少,还有多少没下载。

4.6.3 发送

还在循环里。

69 #发送70 msg = header + current 71 smtpserver.sendmail(user, to, msg)
72 print 'sent.' 73 time.sleep(sleep) 74 75 smtpserver.close()

第70行,把邮件头和邮件正文拼在一起。这看起来是很天真的做法,邮件头和正文就这么就拼一起了啊?是的,是SMTP还是什么协议,就是这么规定的。好像head和body中间还有个回车。

第71行,发送出去了。

第72行,避免用户心急,程序把邮件发出去了要告诉用户一声。

第73行,前面提到了,睡一下,避免SMTP服务器认为我们是发送垃圾邮件的坏人。

第75行,离开循环,邮件都发送完了,断开连接。做事要有始有终,保持优雅。

明天,或者后天,讨论一下接收端baoyu.py。

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

博客会手工同步到以下地址:

[http://giftdotyoung.blogspot.com]

[http://blog.csdn.net/younggift]

20121225

两张图片:Visual Studio DSL工具特定领域开发指南 笔记

两张图片:Visual Studio DSL工具特定领域开发指南 笔记

精读了《Visual Studio DSL工具特定领域开发指南》,写了笔记,画了图。拖
了几天,估计还是没时间整理笔记。

以下两张图片,权当笔记了吧。


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

博客会手工同步到以下地址:

[http://giftdotyoung.blogspot.com]

[http://blog.csdn.net/younggift]

耳机收纳制作

耳机收纳制作

没有耳机听电话的时候耳机疼,有耳机呢,耳机线老是卷在一起。所以我用麦当劳的优惠卡做了一个耳机收纳,把线缠上面。用了几天,效果不错。

我得意地掏出DIY的耳机收纳,鞠孙谷三位同学说,"你网上买的啊。"我说,"不是啊,我做的。"又问,"你了网上以后做的啊"。我说,"不是,独立创造的。"

我又得意地掏出DIY的耳机收纳,关郑两位同学说,"你网上买的啊。"我说,"不是啊。为什么你们也这么说。"他俩说,"因为网上有卖的,而且巨便宜,种类也多。"

我搜了下,真是不少,做成各种可爱卡通的。买了几种试用,都不咋地。重要原因是,它们都太小,耳机线缠很多圈,打开的时候费劲。不过,它们提供了很好的设计方案,比如都有个小孔,耳机线进去容易出来难。所以,我就照些改良了我原来的设计。

变成这样。

所用工具如图所示。

缠上以后的效果。

对比的产品。

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

博客会手工同步到以下地址:

[http://giftdotyoung.blogspot.com]

[http://blog.csdn.net/younggift]

用邮件分割和传送大文件,python实现 I

用邮件分割和传送大文件,python实现 I

1. 限制和解决方案

经常有人替我国导演和科幻作者们叫屈,说这样的环境怎么能出好作品呢;也经常有人为自己叫屈,类似他有这样的父母环境怎么能成才呢。不过,我看过一个故事,说法略有不同,名字忘了,应该还是个名篇。

故事里说,有个家伙因为被抢劫,捆得很结实,而又刚刚好能动弹一点,能一跳一跳地走。他被救以后,发现能演小丑什么的,留在马戏团工作。因为绳子约束,力量更大,而且能做很多原来完不成的动作。最后,老板想杀他放出狼来,他做好准备放手一搏,觉得胜券在握。老板娘一刀把他身上的绳子割了,喊"那谁,你快跑啊。"然后就没有然后了。这哥们离了绳子啥也不是,后来是不是被狼咬死,我就忘了。

这个故事告诉我,限制从来不一定是致命的阻碍,有时更可能是个磨砺人的挑战。

包师弟和ZHUMAO聊,说到某单位的网络真是不咋地,下载居然要收费,不过有个漏洞。包师弟说,这个漏洞就是下载邮件的流量不算钱。

这倒是也合理,不然就往网络中心或领导的信箱里成天发大附件就行了。同时,从原理上也说得过去,SMTH的时候,不是我乐意的,相当于接听电话不收费;而POP3的时候,已经是内网了。

ZHUMAO说,这个漏洞可用。他提到,先把大文件切成几份,然后用邮件发给包师递,包师弟根据一定条件收邮件,然后拼起来。

我说:你们应该用PYTHON整。ZHUMAO说:对,你整吧。

于是我就整了。没有写容错部分,一共197行,拿出来讨论一下。

2. 整体框架

整个软件分成两部分,是两个.py文件。一个是发送文件用的,名字叫 zhumao.py,另一个是收文件用的,名字叫 baoyu.py。

2.1 zhumao.py

发文件的zhumao要顺序执行这样一些操作:

* 编码文件

用base64或可打印字符,因为邮件是ASCII文本的,而大部分二进制文件都有ASCII控制字符,比如电影。之所以编码,另一个原因是我不想使用附件,实现的时候更麻烦一些,以后也可以改成用附件。

* 拆分文件

把文件拆成定长的几段,比如10K一段,然后再发送。最后一段可能稍微短一些。

* 标记及百分比,发送

在邮件的subject上做标记,比如 [来自zhumao,好片子] 这样包师弟接收的时候过滤邮件更容易一些。

然后找个SMTH邮件服务器发出去。

* 避免被服务器视为垃圾邮件发送者

邮件服务器可能会因为你一直发一直发认为你是恶意的,所以发一会要 sleep一会儿。对软件性能的影响,就是没有办法的事了。

2.2 baoyu.py

收文件这一端,要执行相反的操作,即顺序执行:

* 检查

用POP3检查所有的邮件头。

* 根据标记过滤,下载并删除

把符合条件的邮件,比如 [来自zhumao,好片子],下载下来,从服务器端删除。在这里,以后应该支持错误检校,出错重传。现在还没有支持,作为原型,就算对付吧,如果1G文件中间有一个坏的,就得重传全部。

* 合并文件

把文件从邮件body中析出来,然后拼成编码的文件。有些邮件服务器会在subject 之后加上乱糟的东西,比如 anti-spam
之类的,这破坏了一个通常的假设,即subject之后一行以后全是正文。这导致析出文件稍微麻烦一些。我为了实现简单,在body的最前面加了标记,后面也加了标记。反正邮件不是用人手,而是用zhumao.py发出的,所以这一部分可以视为
zhumao-baoyu 协议。

* 解码文件

把ascii文件解码为binary。

* 接收百分比

还要显示接收了多少。同时,因为邮件到达顺序可能与发送的不同,所以zhumao.py在发送的时候还在 subject 中还加入了共几包,当前这封邮件是第几包。

2.3 实现

列出上述框架以后,我先写zhumao.py,用 thunderbird 和 gmail 检验发送的内容;然后再写
baoyu.py,此时用已经实现的zhumao.py发送测试用例。因为我不熟悉python,很多语法和底层的机制 (如分割字符串)
,都是一边写一边查的。估计有更优雅的实现方案,不过那就不是我这样的初学者能提供的了。

所以,大牛们读到此处,已经可以评判 zhumao-baoyu 方案了。初学者们请明天再来,我们继续一起学习吧。

此外,这个zhumao-baoyu草稿在计算机网络中也可以视为 smtp-pop3 协议基础上实现的文件传递协议,还可以扩展更多的功能,比如
baoyu 要求列出 zhumao端 提供的文件目录,选择文件下载。在协议之上建立新的协议,VPN中的l2tp、WAN口连接使用的
PPPoE,都与此类似,称为隧道协议。

在隧道协议中,表面上我们谈论的是一件事情,而实际上,我们真正想传递的,往往另有深意。就像年终岁尾,大家喝酒的时候,逼你喝酒的那些人,他表达的是:"你应该表达臣服于我,低个头啥的。"请脑补雄性大猩猩拍胸脯的动作。

突然想起来北京土著喜欢自称为"爷"的习惯,还有四川土著喜欢口头对别人的"婆娘"不敬这两件事,他们可能还会解释为"啊呀,我就是习惯了,没有恶意。"明知对方的感受,不郑重道歉的就是恶意。坏习惯是病,如果能改就改,如果不能改,就得治。我对四川同学说过,你说别人的媳妇啥都行,对我得例外。期待机会哪天对哪位北京同学喊一嗓子,你TM是谁的爷。

终于还是跑题了。

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

博客会手工同步到以下地址:

[http://giftdotyoung.blogspot.com]

[http://blog.csdn.net/younggift]

20121217

Linux shell脚本,Linux下的西红柿时间管理法 IV

Linux shell脚本,Linux下的西红柿时间管理法 IV

续 清理

本来以为三次就写完了,用了几天这两个脚本,发现了一点问题,所以还得再说
两句。希望这次就真的结束了。

我习惯不用计算机的时候,扣上盖,然后机器休眠。这个习惯与上述西红柿时间
管理结合在一起产生了一个问题:at任务还在继续,再唤醒机器的时候,时间管
理还在继续,可能正进行到第5分钟,也可能是第12分钟,而我希望重新计时。

重新计时也很简单,再执行个20.sh就行了。这么做带来的问题是,这个新的时间
管理起始点建立了,旧的却没有清除掉,所以可能过不了几分钟,旧的休息时间
就到了。

清除掉旧的任务也容易。比如执行 atq 显示当前的任务为:

~ $ 20.sh
~ $ atq
3890 Mon Dec 17 21:31:00 2012 r young
3891 Mon Dec 17 21:32:00 2012 r young

3890和3891就是当前的两个任务。清除的命令是:

~ $ atrm 3890; atrm 3891
~ $ atq
~ $

任务果然清理干净了。

但是每一次都要atq,然后去读那两个任务号,然后再atrm,挺麻烦的。所以,有
了下述脚本,用于清理旧的时间管理任务。

代码如下:

1 #!/bin/bash
2
3 task=$(atq -q r | cut -f1 )
4 if [ -z "$task" ]; then
5 echo "No task in queue r."
6 exit 0
7 fi
8
9 for t in $task
10 do
11 atrm $t
12 done

第3行,用atq显示任务列表,然后管道给cut,得到第1列,也就是任务号,看起
来的效果就是:

3890
3891

这些数据赋值给了变量 task。

第9行,遍历数组task,对于其中的每个变量 t ,应用atrm操作,相当于:

atrm 3890
atrm 3891

这样,每当我开机的时候,不必再atq看是否有任务,然后再记住任务号什么的,
只要执行 clear_at.sh,旧的任务就清理掉了。

既往不咎,一切重新开始。

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

博客会手工同步到以下地址:

[http://giftdotyoung.blogspot.com]

[http://blog.csdn.net/younggift]

20121209

那些年,我们一起喜欢的诺基亚

那些年,我们一起喜欢的诺基亚

我的手机旧得不行了,只好换了个新的。旧的不行的意思,如同现在的小青年所说的,并非拿起来就散架,而仅指"感觉"不那么好了。每天都要充电,下午的时候就会耗干净自动关机,打开通讯录会停在那里几分钟……诸如此类。

这个淘汰掉的手机,买的时候也2000多元,现在不用,甚是心疼。当初买的时候是奔诺基亚去的,立场不坚定,临时决定换成了MOTO,windows操作系统的。于是,两年多来,我经常要对别人说"这不是黑莓,这只是长得像黑莓的摩托罗拉。"

此前,我的手机是诺基亚1110那种直的,小手机。再之前……时光如流水,令人感叹。

我的第一个手机是大学四年级的时候买的。当时在外面打工,我有个BP机,经常会收到"短信"之类的,那都是上帝发来的,表示有活了。然后我要跑到电话亭--有人值守的,和美国电影里的不一样,交上5角钱,把电话打回去。我买手机的主要原因,是电话亭上全写着大字"肆角",但是值守的人总是要收伍角钱。更早的时候,上面的玻璃上写的是"叁角",你猜对了,值守的人一定要收肆角。

我对每次要跟人讨论再被拒绝非常愤怒和疲惫,于是买了一个手机。当时我想的是,反正每个电话5角,到哪里打都是。后来二猫妈提醒我,电话亭是每三分钟5角,手机是每分钟5角,而我的每个电话通常不止一分钟。不过手机当时早就买完了,我只好感叹我的算术有多么的差。

第一个手机是Panasonic的,黑的,有现在两个手机那个厚,纯英文的。纯英文没有造成任何干扰,因为当时没有人给我发短信。倒是Panasonic这个名字,每当我提起来,就有人说,"不就是松下么。"确实就是。不过我用英文不是特意显摆,而是在那手机之前,我对日本的这些公司还分不太清,天天见到手机上写的这个词,就记住了。

后来,还用过一个摩托罗拉的,当时它还没有萌到改名叫MOTO。紫色的,形状像个猪腰子。我记得本来是二猫妈的,女式的。后来联想庆祝飞船上天征文接龙,评委说书人给了我一次一等奖,奖品是个鲜红的联想掀盖手机。联想手机给了二猫妈,换下来猪腰子归我用了不少时间,直到换成诺基亚1110那种。那个联想手机为我对国产品牌的见解积累了一些素材,彩屏的,但是除了彩色,别的跟黑白手机没有任何两样。

那时候,联想好像还不叫Lenovo,就叫联想。中央电视台的广告里,一双手推开两扇窗,外面是蓝天白云,低沉而洪亮的男中音说,"人类失去联想,世界将会怎样。"最终,人类失去了联想,世界也没有怎么样,还是那样。而且,让人类失去联想的,正是Lenovo自己。

我拿着非智能黑白的手机去的芬兰。某次,师姐向我显摆她的智能彩屏手机。她说,她的手机能听歌,我说我有MP3;她说,她的手机能拍照,我说我总随身带个相机;她说,她的手机能看电子书,我说那太对眼儿了,累挺;她说,没事的时候还能打个游戏呢,我说,哪有没事的时候啊。

我当时还不知道诺基亚能砸核桃,不然真应该拿出来跟智能彩屏手机比较一下。

最终,为了能用网络,能gtalk,我换成了MOTO。当google离开中国的时候,我消沉抑郁了挺长时间。后来,gtalk经常联不上,我就成天开着软件,它自动尝试登录,失败,再尝试登录,再失败,再尝试。大部分GPRS流量,都这样消耗掉了。能连上GTALK的朋友几乎看不到我说话,只是大部分时间掉线,偶尔上来下去的折腾。他们告诉我还有QQ和飞信微信这些方式,但是那些都不是我换成MOTO的原因。

我守着MOTO,一共用了两块电池,直到每天充电,通讯录也打不开了。后来,它放弃了我。

最近,我买了诺基亚X2。不错的手机,只是GTALK的时候必须就只GTALK,别的什么也不能干。它不支持多进程。GOOGLE日历也不能用。

还是最近,我又买了诺基亚E71。不错的手机。GTALK能用了。GOOGLE日历还不能用。随机配了张光盘,里面有个软件叫做PC套件。网上大家都说,装个PC套件就好了,可以把手机日历与OUTLOOK同步,然后OUTLOOK与GOOGLE日历同步。

可是,PC套件安装失败,因为证书过期。我刚买的手机,光盘里的软件证书就过期了。网站很难连接,连接了说不对本地区提供下载。有人说,是因为诺基亚重组改生产线什么的。我知道,就是因为Symbian不再能创造价值,被退休了,不再有经费维护。

这些年来,诺基亚陪伴过我们大家,跟我们一起度过这样的一个时代,喧嚣而迅速。但是它最终的命运不过如此。我们把所有的东西都抛在身后,包括我们自己,努力狂奔。我们还要大叫,"我就是这样的。"好像如果不抛掉自己就不再是自己了。

我见到过朋友们换了很多手机,换了电话号码。其中有一些逐渐失去了我的消息,直到他们需要我的那一天。而我,永远失去了其中一些人的消息。每一次换手机或者电话号码,我都群发短信,我相当长时间保留着旧电话号,并让它开机。电话号码,不仅是当你需要别人的时候,更重要的,是别人需要你的时候,它能有效。我一直保持着24小时开机的习惯,我一直在这里。

我见到IT公司们风起云涌,爆炸一样眩目,然后又消失在人们的视线里。我见到各种技术出现和衰落,有人预言在将来的多少年里它会流行得一踏糊涂。请各位根据自己的人生经历自行脑补以上这两段的实现。渐渐的,我不再关注现在,只关注过去和未来。现在如此短暂,实在不值得为此刻的任何欢愉浪费哪怕一个小时的时间。

冬夜越来越长。雪还是下个没完,长春已经不再关注清雪了,反正明天还是要下。路上积了厚厚的一层,黑的白的灰的。路灯下,清洁工人穿得像一群桔红色的熊,伸直胳臂向四下里撒溶雪盐。公路会在几天后融化出灰黑色的路面,人行的路正变成越来越大的雪堆。

这样的冬夜里,我想起小学骑车路过的水泥厂旁边的河和路。那里的雪是一层一层的,一层白,是雪,一层黑,是水泥厂的灰尘。骑车通过的时候,冬天要格外小心,因为路上的冰上全是汽车的车辙。得完全按车辙骑,一不小心骑歪了,就会摔倒。

夏天回通化的时候,我看到那条路已经修得我完全认不出来了,平整宽阔,旁边的水泥厂拆了,立在那儿的是个不错的小区。真是很好,只是我没有与它一起生活的这段时间,它变得令我如此陌生。

很多这样的东西都会弃我们而去,除了冬天这样的小路,除了我的MOTO,还有黑岛游戏小组,还有westwood,还有那些年我们喜欢过的诺基亚。

说起黑岛,起初它并不是游戏小组的名字,而是《丁丁历险记》里的一册。Star Trek
里的库克船长,最初可能是那位真的船长,在凡尔纳的《机器岛》里提到过,他被东南亚或澳大利亚的土著杀了,可能还吃了。不过,他们以另一种方式活了下来。黑岛、库克船长,喜欢它们的那些人长大,变成牛人,然后把它们以另一种形式复活,在自己的作品中生活下去。

《那些花儿》里唱,"她们都老了吧,她们在哪里呀"。她们也许在世界的哪个积满灰尘的角落里哭呢,也许早就死去了。但是,我真的希望,读着这一篇博客的某一位,也许你将来发达了。到那时,请不要忘记陪伴过你的诺基亚。请……把它买下来,请让它的名字在另一种产品中复活,让又一代人会喜欢它。

也许,单纯地为了能让那些我们喜欢的继续或重新陪伴我们,我们不得不努力,既使这努力令我们如此痛苦。


又及。昨天读孟子那段著名的话,"君视臣如草芥,臣视君如寇仇。"原来还有一段在后面。王问,"听说有臣下在离职以后君主死了,臣下还为君主服孝的,这怎么整的啊。"孟子说:"君主在臣下离职以后三年,还给他留着房子;臣下要去哪,送到国境,还派人把臣要去的新地方打探安排好。这样的君主,臣下自然会服孝。像那种人走灯灭,刚说要辞职就收回房子的,还指望臣下服孝呐?"

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

博客会手工同步到以下地址:

[http://giftdotyoung.blogspot.com]

[http://blog.csdn.net/younggift]