可可熊的窝

Tag Archives: Python

PyFetion 0.2 发布

IN:Life   Tags: ,    Comments:52

PyFetion 0.2版本发布,协议根据移动09.11.04的飞信版本:Fetion2008 3.5.2(安全加强版)

http://code.google.com/p/pytool/
更新内容包含:

  1. 增加查看飞信好友是否隐身功能
  2. 增加登录时状态的选择[隐身 在线 忙碌 离开]
  3. 日志改用Python的logging模块
  4. 增加对好友状态改变的处理( 如上线等)
  5. 重写TCP方式中的底层通信函数
  6. 使用对列保存接收到的多余消息( 例如发短信时本来应该返回200 OK 却先来了个BN通知消息,以前这样会出错,现在底层会把BN消息放在队列中,返回200 OK)
  7. 修改了一些异常处理方式
  8. 增加登出,删除好友函数
  9. 改写get_contactlist函数,使用一个dict保存当前的好友列表
  10. 增加一个receive函数 做客户端的时候可以在一个线程中主调用该函数,所有的消息都会yield出来(请参考fetion.py)
  11. 修正向PC发送消息的命令,飞信新增加了一个CatMsg的命令
  12. 增加接收从最新版本PC端发送的消息功能;这个比较麻烦新版本飞信对每一个新会话使用fork出一个线程的方式;
  13. John先给服务器说我要开个新会话,服务应答一个消息说,你去这个IP吧,带着这个密钥
    于是John就连到了这个IP,并使用这个密钥登录,接着发一个包含Bob的uri的邀请命令;
    服务器把这个通知(包含IP 密钥和邀请者John的uri)给Bob,Bob收到服务器的通知后也用同样的密钥登录该IP
    这时John收到Bob进入会话的消息,他就开始正式发消息了

  14. 许多清理了修正
  15. 调整类的结构
  16. 改用MIT License
  17. 增加了一个CLI的飞信客户端 跨平台支持
  18. Fedora8 Python2.5.1测试;Windowx XP Python2.6.4测试;Win7 Python2.6.2测试;Mac 10.5.7 Python2.5.1
  19. 我忘记在这里列出来的

./fetion.py
————————基于PyFetion的一个CLI飞信客户端————————-

命令不区分大小写中括号里为命令的缩写

help[?] 显示本帮助信息
ls[l] 列出好友列表
status[st] 改变飞信状态 参数[0隐身 1离开 2忙碌 3在线]
参数为空显示自己的状态
msg[m] 发送消息 参数为序号或手机号 使用quit退出
sms[s] 发送短信 参数为序号或手机号 使用quit退出
参数为空给自己发短信
find[f] 查看好友是否隐身 参数为序号或手机号
add[a] 添加好友 参数为手机号或飞信号
del[d] 删除好友 参数为手机号或飞信号
cls 清屏
quit[q] 退出对话状态
exit[x] 退出飞信

fetion.py特色:

  1. 多线程支持,同时收发消息
  2. 添加,删除,好友,判断好友是否隐身功能
  3. 占用资源少,我正写这博客的时候官方的飞信占我96.8M的内存
  4. 跨平台支持
  5. 扩展性好,加两行代码就可以实现从手机发命令关机等功能
  6. 其它我没发现的
12-12
2009

Google Map

IN:Python   Tags: ,    Comments:5

Google Map的算法分析见:http://www.codeproject.com/KB/scrapbook/googlemap.aspx

Google Map使用http://mt[0-3].google.cn/mt/v=cn1.11&hl=zh-CN&x=%d&y=%d&z=%d&s=Galile这样的URL来表示一个放大等级中最小的地图分块。
mt0—-mt3是四个Google Map的服务器
v=cn1.11 是当前地图的版本 一直在变化中
x,y,z分别代表当前分块所在位置和放大等级,Z=19为最大的放大等级,目前很多城市都可以查看到该等级下的地图
在某个放大等级z下,整个地球被分成2^z分块
x,y可以根据经纬度来计算;x的计算比较好理解(longitude->经度):
longitude=180+longitude #修正经度值到0-360 因为经度的表示是从-180—>180
longTileSize=360.0/(pow(2,zoom)) #计算每个分块所占的角度数
tilex = longitude/longTileSize #计算当前经度所在的分块位置

y的计算要牵扯到墨卡托投影这个地图算法,比较麻烦没看懂。不过并不影响使用,相应的代码上面已经给出来了,然后就可以用Python下载地图分块,使用PIL库把分块合并。

代码见Google Code

(这个是西安地图):
#GMap2Png(108.80824,34.37075,109.10316,34.15366,16)
参数为你需要确定的图片左上角的经纬度和右下角的经纬度 最后一个是放大的等级
使用的时候需要注意如果你选择的经纬度范围较大,那么放大等级就不能太大,不然要生成一个巨大图片,PIL会报MemoryError的错误。

还有些问题我在CPyUG记录了下来,不过没使用好的解决方案。

08-04
2009

多资源、多线程、断点续传

IN:Python   Tags:    Comments:16

我要做一个支持多资源、多线程、断点续传的小工具,基于Python。Windows下有迅雷、FlashGet、QQ旋风(相当地泛滥),Linux下也有D4X,Axel ,aria2,那为什么还要写这样一个工具呢?

(1) 上面都不是Python写的。
(2) Linux下的下载工具都不能主动获取资源,而现在迅雷和FlashGet之所以快就是因为他们有资源。
(3) Linux下几个工具多线程、断点续传做的不太好。aria2似乎不错,不过我没编译过去,支持那么多协议,觉得有点臃肿。

其实从原理上讲,迅雷不过就是一个HTTP/FTP的多线程断点下载工具,现在做大了就来抢BT、电驴的用户,把这两个协议也加了进去。

前段时间看过axel,它的实现太简单了,只是固定的产生N个线程,然后把文件分成N块交给这些线程去下载。从迅雷的配置文件和下载的日志中可以看出来迅雷对下载文件的分块和线程的使用是有自己独有的方式(它线程下载的数据块有时很大4M,有时很小32K,即使在同一个线程中也是这样),我没办法知道迅雷是怎么做这个工作。所以我只能通过猜测和自己的一些想法来做这个工作。尽可能的保证合理的分块和线程使用。

创建一个下载管理器,包括配置信息,任务列表,一个具有速度值与使用次数的URL列表。

首先得到下载文件的大小(filesize),然后根据配置以及获得的URL数(N)对文件进行分块。
设置一个最小的分块值 MinPiece = 1024*128 (最小分块大于filesize的5%)
设置一个最大的分块值 MaxPiece = 1024*1024*200

if filesize < = MinPiece:
不进行分块。选第一个地址进行下载,如果该地址下载超时则使用其它地址下载。(是否可以考虑使用N个地址同时下载?)

elif:
把文件分为N块,每块大小为Piece。
if Piece < MinPiece
把文件分为N-1块,Until Piece > MinPiece

if Piece > MaxPiece
把文件分为N+1块,Until Piece < MaxPiece

生成任务列表,一个任务包括:URL,filename,range[start,pos,end]
任务列表中对任务进行标记,DONE,RUNNING,PAUSE,STOP(需要这么多吗?)

根据获得的资源数产生N线程的线程池。

逐步向线程池中增加线程:前提是所有线程成功运行,则增加一个线程。
每个URL对应一个线程的上限数(5),总使用线程数固定(35)

线程运行时检查是否有空闲线程,如果有,则把自己的任务分割(分割的办法使用折半方式,同时要保证分块大于MinPiece),放在任务列表中,同时修改自己的任务的分段信息。
加入任务列表时从URL列表中选一个速度较快的URL,且该URL线程数未达到上限。(如何判断是否达到服务器支持连接数上限?如果针对某个URL创建新连接失败则标记该URL为不可再连接)

线程运行时检查缓冲区的大小,当大于write_buffer_size时则写文件(大于文件的10%时也写?),并且写记录文件,更新URL列表中的速度值,做这些操作时要使用锁机制。这个write_buffer_size根据不同的文件对应不同的大小(2M--->16M迅雷使用8-256)。写文件时可以直接seek到需要写入的位置,如果操作系统不支持这种方式,则先用0填充文件。

线程对失效的URL进行标记,对不支持断点续传的URL进行标记。考虑GBK,UTF-8编码。
线程完成时与停止时做写文件操作与上面操作相同。

记录文件中保存任务列表:
URL,filename,range[start,pos,end]
这样续传的时候程序就根据记录文件载入所有的任务,然后开启线程,进行下载。
记录文件使用MD5校验(最后一行存放前面信息的MD5值)。

一个线程如果速度过慢,则释放当前的任务,把它扔回任务列表中,标记该URL很慢,由空闲的线程再次开启这个任务,这时候有可能又由这个线程接管,但是使用的URL不同,因为扔回任务列表的时候会先一个较快的URL。

HTTP请求需要设置一个超时时间。

———————————————————————-
只是把我的一些想法在这里先记录下来,所以比较乱,做的时候可能还会有些变化。至少我目前觉得这种设计方式还是比较智能和高效的,欢迎大家拍砖,如果有好的建议我会记录下来。

06-02
2009

pythunder又写了一遍

IN:Python   Tags: ,    Comments:27

前几天在Python中文件邮件列表里看到讨论使用Python实现迅雷,后来又在Ubuntu论坛看到一个贴子,实现了获取迅雷服务器上的侯选资源(不是开源的)。突然就对这个迅雷协议很感兴趣,在别人分析的基础上用Python写了个小工具,与Ubuntu论坛上那个作用是一样的,获取迅雷服务器上的侯选资源。

迅雷有多个资源服务器,有的资源服务器返回完全一样,有的返回某些一样,这样可以指定多个服务器,获取更多的资源,同时也可以防止某个服务器不能正常响应。

用webpy写了个演示面面:

http://cocobear.info/demo/pythunder/?url=http://3.duote.com/xunleidt.exe

不知道开源了会不会产生麻烦?

———————————解释下标题——————————
前几天拿U盘做产量的时候没把代码备份出来,所以原本已经写好了今天又写了一遍,幸好代码很短:-)

05-04
2009

关于PyFetion不能在GAE上使用

IN:Python, 编程相关   Tags: , ,    Comments:44

最近有同学反映PyFetion不能在GAE上使用,我看了下别人给出的错误,问题不出在PyFetion上,无论是TCP方式还是HTTP方式,使用本机测试都可以正常工作。
应该是移动的飞信服务器屏蔽了GAE的出口IP地址(某同学反应GAE的urlopen使用的是同一IP)。

原因大概是广大GAE的爱好者众多,玩PyFetion的又比较多,所以移动不爽了,就封掉你。

问题出来了,原因也大概知道了,那怎么解决方案呢,下面就给出几个吧:

  1. 强烈要求GAE增加代理支持。
  2. 打电话给10086,然后说你的飞信上不出了,然后给客服MM说你的IP是:[GAE_IP(哪位知道这个IP来补充一下)]。
  3. 脱离GAE,使用自己的服务器。
  4. 继续使用GAE,使用另一服务器做为跳板。
  5. 解释一下,GAE不能直接访问飞信服务器了,可以在另一个地方,比如cocobear.info/PyFetion.py?phone=13838381438&pwd=383838&msg=^_^
    做这样的一个的接口,然后使用GAE去访问这个服务器。

Update(09.4.27):
据gohsy同学反映,GAE上不能使用是因为服务器返回了405错误,而别的fetion实现会忽略405错误,继续进行,而PyFetion遇到这个错误会产生异常。记得以前调PyFetion的时候也遇到过405错误,应该是发送的数据格式不对而造成的,所以我猜想这里是因为网络环境影响造成的数据不完整,从而使得飞信服务器返回405错误。因为在GAE上PyFetion还是可以偶尔发送成功的,所以出错的原因应该不是程序本身。

为了在GAE上能更好的使用,我修改了一下PyFetion,增加了遇到405错误时重试功能,默认重试5次,这样会使得发送的成功率提高很多,但是由于405的出现,重试多次会影响到发送的速度,这一点是无法避免的。更新请到google code查看:http://code.google.com/p/pytool/source/detail?r=30

其实根本的原因我想还是GAE上用PyFetion的人过多造成的:-)

为了在GAE上调PyFetion,在gohsy同学的帮助下,搭建了GAE环境,感觉还不错,做应用挺方便的,不错的东东。

04-25
2009

Python与Lua中的尾部调用优化

IN:Python   Tags: ,    Comments:3

>     function foo (n)
>>       if n > 0 then return foo(n - 1) end
>>     end
> foo(100)
> foo(1000)
> foo(10000)
> foo(100000)
> foo(1000000)

如果函数最后一句是return g(…)这样的形式,Lua将会把这句解释为goto g(x),因为这里除了对g函数调用,再没有别的事做,也不需要保存堆栈里调用函数的信息。因此上面即使调用很多次也没有出现堆栈溢出的问题,把上面的代码转换为Python:

>>> def foo(n):
...     if n>0: return foo(n-1)
...
>>> foo(100)
>>> foo(1000)
...
  File "<stdin>", line 2, in foo
  File "</stdin><stdin>", line 2, in foo
  File "</stdin><stdin>", line 2, in foo
RuntimeError: maximum recursion depth exceeded

很快出就现了堆栈溢出的问题,在这里表现为达到了递归调用的最大限制。Python自身没有实现尾部调用优化,不过也可以通过实现一个decorator办法来实现:

#!/usr/bin/env python2.4
# This program shows off a python decorator(
# which implements tail call optimization. It
# does this by throwing an exception if it is
# it's own grandparent, and catching such
# exceptions to recall the stack.

import sys

class TailRecurseException:
  def __init__(self, args, kwargs):
    self.args = args
    self.kwargs = kwargs

def tail_call_optimized(g):
  """
  This function decorates a function with tail call
  optimization. It does this by throwing an exception
  if it is it's own grandparent, and catching such
  exceptions to fake the tail call optimization.

  This function fails if the decorated
  function recurses in a non-tail context.
  """
  def func(*args, **kwargs):
    f = sys._getframe()
    if f.f_back and f.f_back.f_back
        and f.f_back.f_back.f_code == f.f_code:
      raise TailRecurseException(args, kwargs)
    else:
      while 1:
        try:
          return g(*args, **kwargs)
        except TailRecurseException, e:
          args = e.args
          kwargs = e.kwargs
  func.__doc__ = g.__doc__
  return func

@tail_call_optimized
def factorial(n, acc=1):
  "calculate a factorial"
  if n == 0:
    return acc
  return factorial(n-1, n*acc)

print factorial(10000)
# prints a big, big number,
# but doesn't hit the recursion limit.

@tail_call_optimized
def fib(i, current = 0, next = 1):
  if i == 0:
    return current
  else:
    return fib(i - 1, next, current + next)

print fib(10000)
# also prints a big number,
# but doesn't hit the recursion limit.

代码来自http://code.activestate.com/recipes/474088/

在这段代码里tail_call_optimized是一个decorator,在执行factorial函数前,这个decorator先执行,tail_call_optimized中通过sys._getframe()这个方法会返回一个frame对象,包含了堆栈顶部的信息,当发现当前调用是一个递归调用:

   if f.f_back and f.f_back.f_back
        and f.f_back.f_back.f_code == f.f_code:

则抛出一个异常,下面的代码则截获异常,继续执行,这样就避免了堆栈的使用,很巧妙的一种方式。

在python-dev的邮件列表里,有人曾经做过一个Python的尾调用优化的补丁,不过Guido拒绝了这个补丁:

I’m not interested in adding this to the official Python release.

One reason is that if an exception happens in such a tail-recursive
call, the stack trace will be confusing.

Another reason is that I don’t think it’s a good idea to try to
encourage a Scheme-ish “solve everything with recursion” programming
style in Python.

But feel free to maintain this as an independent modification, a la
Stackless — I’m sure there are people who would like to try this
out.

–Guido van Rossum (home page: http://www.python.org/~guido/)

03-31
2009
Page 2 of 712345...Last »
loading...