现在的位置: 首页 > 综合 > 正文

iocp的python封装(续)

2013年08月29日 ⁄ 综合 ⁄ 共 2321字 ⁄ 字号 评论关闭

原文地址:http://darkbull.net/python/bca/pyiocp/

题记

        以前用C++封装过iocp的python模块,写过两篇文章。尽管当前公司的项目最终将部署在linux上,但现在的开发是在window上。用select模型写了个简单的net io wrapper,但好像很不稳定,老是出现一些莫名其妙的问题。这两天,决定把这以前写的pyiocp代码翻出来,整整再用。但当我打开项目的时候,我晕了(大脑缺氧,极需美女给我做人工呼吸!)好久没用C++了,本来我的C++基础就不咋的,再加上用了这么长时间的python,看以前的C++代码尽然会头晕,虽然有详细的注释,但似乎还是回忆不起来。!@#$$#!@ 以前封装的pyiocp,只在公司项目的测试中使用过一段时间,问题也比较多,没用多久就废了。这两天时间,我用C语言又重新写了一个封装。使用的是gcc。本来想让代码在vs下也能编译通过,但后来发现vc2008对c99的支持极差,所有的变量都必须放在块语句开始处、执行代码的前面。我无语,不想对代码做大调整,一来怕麻烦,二来我觉得把变量放在块语句的开始处会降低代码的可读性。于是不再提供在vc的支持。

实现模式

        关于IOCP的资料在网上有一卡车一卡车的,这里我不再做重复的劳动,只分享一下pyiocp的实现原理。第一次封装的时候,有一张图来描述使用pyiocp收发数据的过程:

        由上图可以看出,服务器接收、发送的数据都通过pyiocp。在实际使用过程中,我发现这种实现模式有一个问题:游戏服务器发送的数据包,一般都比较小(最大的也不过几K),而每个socket的低层协议栈缓冲区大小默认是32K,除非出现下面两种情况会使缓冲区填满而收到相应的socket错误,一是在极短的时间内发送的数据量超过32K,二是客户端的网络极差,放在缓冲区里的数据半天也没发出去。第一种情况在我们的游戏服务器中是不会出现的,而对于第二种情况,如果不及时的关闭这种连接,那么发送给这个连接的数据将不断的堆积在内存里,使内存占用越来越大。所以,我觉得没有必要通过pyiocp发送数据,这样增加了程序的复杂性的同时,也没带来多少好处。索性直接通过socket发送,如果发送的时候收到socket错误(在send时很少会发生错误,即使出错,缓冲区满是主要原因),就把该连接断掉。我曾经读我领导写的代码时,他就是这么干的。所以,新设计之后的pyiocp内部工作模式是这样的:

        除了上面讲的实现模式不一样之后,新封装的pyiocp使用了内存池。内存池是一年前写的,实现参考了python源码的内存池实现。

pyiocp模块的使用

        下面是pyiocp模块提供的方法:

  • init(): 初始化内部使用的资源。在调用其他函数之前,必须先调用该方法。如果调用成功,返回0,否则,返回一个错误码,可以通过errinfo(errno)来查看错误码的描述。一般在程序启动的时候调用该方法。
  • cleanup(): 释放内部使用的资源。
  • run(ip, port): 启动pyiocp监听。参数ip为字符串,表示绑定的本地ip地址,参数port是一个整数,表示监听的端口。函数调用成功,返回0,否则返回一个错误码。
  • stop(): 停止监听。调用该方法后,原先在事件队列中未处理的事件将被清空。
  • send(fileno, data): 发送数据。参数fileno表示socket句柄,data为发送的数据。上面讲到数据是直接发送给客户端的,那么与这里讲的岂不矛盾。其实,send内部实现只是简单地调用winsocket的send()方法,而没用使用WSASend投递。所以并不矛盾。函数返回0表示数据发送成功,否则应该通过errinfo(errno)来查看错误信息。
  • close(fileno):关闭连接。参数fileno表示将要关闭的socket句柄。函数返回0表示操作成功,否则应该通过errinfo(errno)来查看错误信息。
  • event():获取一个事件。如果队列中没有事件,返回None。事件是一个包含三个元素的元组,[0]表示事件的类型,0: NET_NEW,1: NET_LEAVE,2: DATA_NEW。[1]表示对应的socket句柄。[2]表示相应的数据,NET_NEW事件的数据是ip,port,NET_LEAVE事件的数据为None,DATA_NEW事件的数据为二进制字符串。
  • count(): 返回当前连接的数量
  • errinfo(errno): 查看错误码对应的信息。

示例

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pyiocp
import time

NET_NEW = 0
NET_LEAVE = 1
DATA_NEW = 2

pyiocp.init()
pyiocp.run('127.0.0.1', 1234)
while True:
    event = pyiocp.event()
    if event is None:
        time.sleep(0.1)
        continue
    
    type, fileno, data = event
    if type == NET_NEW:
        ip = data
        print '---->', ip
    elif type == NET_LEAVE:
        print '<----'
    else:
        print 'recv data:', data
        
pyiocp.stop()
pyiocp.cleanup()

源码下载与编译

       pyiocp_new:使用gcc编译,在编译的时候,修改makefile中的python版本

       pyiocp_old:使用c++编写,里面有vs与CodeBlocks的项目文件

抱歉!评论已关闭.