文档章节

python模块介绍-asynchat 异步socket命令/响应处理器

python测试开发人工智能安全
 python测试开发人工智能安全
发布于 2014/01/07 07:40
字数 2179
阅读 14834
收藏 83

该模块基于asyncore简化了异步客户端和服务器,并使其更容易元素处理由任意的字符串结束,或者是可变长度的的协议。它提供了抽象类async_chat,提供collect_incoming_data()和found_terminator()方法。循环和asyncore的一样,有2种信道:asyncore.dispatcher和asynchat.async_chat,可以自由混合信道。通常asyncore.dispatcher服务器通道在接收到连接请求时产生新的asynchat.async_chat通道对象。

接口文档

  • class asynchat.async_chat:这个类是asyncore.dispatcher的抽象子类。一般使用其collect_incoming_data()和found_terminator()方法,asyncore.dispatcher的方法都可以使用,尽管不是所有在消息/响应上下文都有意义。

    • async_chat.close_when_done()推None到生产者FIFO。当此None弹出的fifo时,通道将关闭。

    • async_chat.collect_incoming_data(data):数据到达。默认引发NotImplementedError异常。使用时必须重载。

    • async_chat.discard_buffers():在紧急情况下方法将丢弃在输入和/或输出缓冲器和生产者FIFO持有数据。

    • async_chat.found_terminator():当输入的数据流相匹配set_terminator()中设定的终止条件时调用。默认引发NotImplementedError异常。使用时必须重载。缓冲输入的数据在实例属性是可用的。

    • async_chat.get_terminator():返回当前通道的结束符。

    • async_chat.push(data):往通道的FIFO中添加数据,当然可以使用自己制作的更复杂的方案来实现加密和分块之类的。

    • async_chat.push_with_producer(producer):往通道的FIFO中添加数据生产者FIFO中添加生产者。当所有当前生产者队列用尽之后将调用其more()方法并发送数据到远端。

    • async_chat.set_terminator(term) 设定终止符号。类型如下:

    • string: Will call found_terminator() when the string is found in the input stream。

    • integer: Will call found_terminator() when the indicated number of characters have been received。

    • None: The channel continues to collect data forever。

    • ac_in_buffer_size异步输入缓冲区的大小(默认4096)。

    • ac_out_buffer_size异步输出缓冲区的大小(默认4096)。

    • 像asyncore.dispatcher, async_chat定义了一组由select()调用后的socket条件分析生成的事件。轮询循环开始后async_chat对象的方法被事件处理框架调用,不需要程序员干预。。两个类的属性可以修改,以提高性能,甚至节省内存。

    • 不像asyncore.dispatcher, async_chat允许您定义生产者的先进先出队列(FIFO)。生产者有唯一的方法中more(),返回通道上要传输的数据。生产者没有数据时返回空字符串。这时async_chat对象从FIFO移除生产者并开始使用下一个生产者,如果有的话。当生产者FIFO为空时handle_write()什么都不会做。通道对象的set_terminator()方法来识别远端的结束,重要的断点,输入。

    • 为了建立一个有效的async_chat子类,方法collect_incoming_data()和found_terminator()必须处理该通道异步接收数据。相关方法如下:

  • class asynchat.fifo([list=None]): 辅助类asynchat.fifo的方法如下:

    • is_empty(): Returns True if and only if the fifo is empty.

    • first(): Returns the least-recently push()ed item from the fifo.

    • push(data): Adds the given data (which may be a string or a producer object) to the producer fifo.

    • pop(): If the fifo is not empty, returns True, first(), deleting the popped item. Returns False, None for an empty fifo.

HTTP实例

下例说明如何通过async_chat读取HTTP请求。 Web服务器可以为每个传入客户端连接创建一个http_request_handler对象。请注意, 初始的信道终结符设置为HTTP头的末尾空行和一个表示正在读取头的标志。

报头读完后,如果请求的类型是POST的(这表明还有其他数据),则内容长度:头用来设置数值终止符。

数据整理完毕, 设置terminator为None,确保web客户端不会读取多余的数据之后调用handle_request()进行数据处理。

class http_request_handler(asynchat.async_chat):
    def __init__(self, sock, addr, sessions, log):
        asynchat.async_chat.__init__(self, sock=sock)
        self.addr = addr        self.sessions = sessions        self.ibuffer = []
        self.obuffer = ""
        self.set_terminator("\r\n\r\n")
        self.reading_headers = True
        self.handling = False
        self.cgi_data = None
        self.log = log    def collect_incoming_data(self, data):
        """Buffer the data"""
        self.ibuffer.append(data)
    def found_terminator(self):
        if self.reading_headers:
            self.reading_headers = False
            self.parse_headers("".join(self.ibuffer))
            self.ibuffer = []
            if self.op.upper() == "POST":
                clen = self.headers.getheader("content-length")
                self.set_terminator(int(clen))
            else:
                self.handling = True
                self.set_terminator(None)
                self.handle_request()
        elif not self.handling:
            self.set_terminator(None) # browsers sometimes over-send
            self.cgi_data = parse(self.headers, "".join(self.ibuffer))
            self.handling = True
            self.ibuffer = []
            self.handle_request()

注意:这并不是一个不添加代码就可以实际运行的实例。

ECHO实例

下面实例来源于《The Python Standard Library by Example 2011》,更详细。

asynchat_echo_server.py:

import asyncoreimport loggingimport socketfrom asynchat_echo_handler import EchoHandlerclass EchoServer(asyncore.dispatcher):
    """Receives connections and establishes handlers for each client.
    """
    
    def __init__(self, address):
        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.bind(address)
        self.address = self.socket.getsockname()
        self.listen(1)
        return

    def handle_accept(self):
        # Called when a client connects to our socket
        client_info = self.accept()
        EchoHandler(sock=client_info[0])
        # Only deal with one client at a time,
        # so close as soon as the handler is set up.
        # Under normal conditions, the server
        # would run forever or until it received
        # instructions to stop.
        self.handle_close()
        return
    
    def handle_close(self):
        self.close()

asynchat_echo_handler.py: EchoHandler继承asynchat.async_chat,收发可以自动处理,不过需要处理如下东东:

  • 处理输入数据,重载handle_incoming_data()

  • 认识输入数据的结束符,通过set_terminator()

  • 收到完整信息后的处理:found_terminator()

  • 发送数据push()

该示例应用程序有两种工作模式。它要么等待像"ECHO length\n"之类的命令,要么等待要显示的数据,通过实例变量process_data切换模式。一旦找到完整的命令,处理程序切换到消息处理模式,并等待接收的完整文本。当所有的数据可用时,它被推入到输出信道。数据被发送处理程序被设置为关闭。

import asynchatimport loggingclass EchoHandler(asynchat.async_chat):
    """Handles echoing messages from a single client.
    """

    # Artificially reduce buffer sizes to illustrate
    # sending and receiving partial messages.
    ac_in_buffer_size = 128
    ac_out_buffer_size = 128
    
    def __init__(self, sock):
        self.received_data = []
        self.logger = logging.getLogger('EchoHandler')
        asynchat.async_chat.__init__(self, sock)
        # Start looking for the ECHO command
        self.process_data = self._process_command        self.set_terminator('\n')
        return

    def collect_incoming_data(self, data):
        """Read an incoming message from the client
        and put it into the outgoing queue.
        """
        self.logger.debug(
            'collect_incoming_data() -> (%d bytes) %r',
            len(data), data)
        self.received_data.append(data)

    def found_terminator(self):
        """The end of a command or message has been seen."""
        self.logger.debug('found_terminator()')
        self.process_data()
    
    def _process_command(self):        
        """Have the full ECHO command"""
        command = ''.join(self.received_data)
        self.logger.debug('_process_command() %r', command)
        command_verb, command_arg = command.strip().split(' ')
        expected_data_len = int(command_arg)
        self.set_terminator(expected_data_len)
        self.process_data = self._process_message        self.received_data = []
    
    def _process_message(self):
        """Have read the entire message."""
        to_echo = ''.join(self.received_data)
        self.logger.debug('_process_message() echoing %r',
                          to_echo)
        self.push(to_echo)
        # Disconnect after sending the entire response
        # since we only want to do one thing at a time
        self.close_when_done()

客户端和处理器类似。要发送的消息先传递给客户端的构造函数。当socket连接建立后,调用handle_connect()发送命令和消息数据。

命令是直接push,消息文本则使用生产者类。生产者有轮询机制把数据块发送到网络。当生产者返回空字符串,写停止。

import asynchatimport loggingimport socketclass EchoClient(asynchat.async_chat):
    """Sends messages to the server and receives responses.
    """

    # Artificially reduce buffer sizes to show
    # sending and receiving partial messages.
    ac_in_buffer_size = 128
    ac_out_buffer_size = 128
    
    def __init__(self, host, port, message):
        self.message = message        self.received_data = []
        self.logger = logging.getLogger('EchoClient')
        asynchat.async_chat.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.logger.debug('connecting to %s', (host, port))
        self.connect((host, port))
        return
        
    def handle_connect(self):
        self.logger.debug('handle_connect()')
        # Send the command
        self.push('ECHO %d\n' % len(self.message))
        # Send the data
        self.push_with_producer(
            EchoProducer(self.message,
                         buffer_size=self.ac_out_buffer_size)
            )
        # We expect the data to come back as-is, 
        # so set a length-based terminator
        self.set_terminator(len(self.message))
    
    def collect_incoming_data(self, data):
        """Read an incoming message from the client
        and add it to the outgoing queue.
        """
        self.logger.debug(
            'collect_incoming_data() -> (%d) %r',
            len(data), data)
        self.received_data.append(data)

    def found_terminator(self):
        self.logger.debug('found_terminator()')
        received_message = ''.join(self.received_data)
        if received_message == self.message:
            self.logger.debug('RECEIVED COPY OF MESSAGE')
        else:
            self.logger.debug('ERROR IN TRANSMISSION')
            self.logger.debug('EXPECTED %r', self.message)
            self.logger.debug('RECEIVED %r', received_message)
        returnclass EchoProducer(asynchat.simple_producer):

    logger = logging.getLogger('EchoProducer')

    def more(self):
        response = asynchat.simple_producer.more(self)
        self.logger.debug('more() -> (%s bytes) %r',
                          len(response), response)
        return response

主程序:

import asyncoreimport loggingimport socketfrom asynchat_echo_server import EchoServerfrom asynchat_echo_client import EchoClient

logging.basicConfig(level=logging.DEBUG,
                    format='%(name)-11s: %(message)s',
                    )address = ('localhost', 0) # let the kernel give us a portserver = EchoServer(address)ip, port = server.address # find out what port we were givenmessage_data = open('lorem.txt', 'r').read()client = EchoClient(ip, port, message=message_data)asyncore.loop()

辅助文本:

# vi lorem.txt 
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec
egestas, enim et consectetuer ullamcorper, lectus ligula rutrum
leo, a elementum elit tortor eu quam.

执行结果:

# ./asynchat_echo_main.py 
EchoClient : connecting to ('127.0.0.1', 58974)EchoClient : handle_connect()EchoHandler: collect_incoming_data() -> (8 bytes) 'ECHO 166'EchoHandler: found_terminator()EchoHandler: _process_command() 'ECHO 166'EchoProducer: more() -> (128 bytes) 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec\negestas, enim et consectetuer ullamcorper, lectus ligula rutrum\n'EchoHandler: collect_incoming_data() -> (128 bytes) 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec\negestas, enim et consectetuer ullamcorper, lectus ligula rutrum\n'EchoProducer: more() -> (38 bytes) 'leo, a elementum elit tortor eu quam.\n'EchoHandler: collect_incoming_data() -> (38 bytes) 'leo, a elementum elit tortor eu quam.\n'EchoHandler: found_terminator()EchoHandler: _process_message() echoing 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec\negestas, enim et consectetuer ullamcorper, lectus ligula rutrum\nleo, a elementum elit tortor eu quam.\n'EchoProducer: more() -> (0 bytes) ''EchoClient : collect_incoming_data() -> (128) 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec\negestas, enim et consectetuer ullamcorper, lectus ligula rutrum\n'EchoClient : collect_incoming_data() -> (38) 'leo, a elementum elit tortor eu quam.\n'EchoClient : found_terminator()EchoClient : RECEIVED COPY OF MESSAGE

参考资料

本文地址


© 著作权归作者所有

共有 人打赏支持
python测试开发人工智能安全
粉丝 182
博文 78
码字总数 348190
作品 0
邵阳
QA/测试工程师
加载中

评论(5)

中华田园犬
中华田园犬
这个文档不错不错
python测试开发人工智能安全
python测试开发人工智能安全
是的,在准备试gevent,greenlet,twisted等
roylieu
roylieu
有点过时了吧,现在都是epoll/greenlet的时代了
Xsank
Xsank
怎么感觉asynchat好冷门
猎户座
猎户座
mark
用Python开发你的第一款聊天软件

在本实验中,我们将实现一个简单的图形界面聊天系统。我们可以通过图形客户端登录聊天室,并与其他成员进行聊天。 本教程由实验楼120发布在实验楼,完整教程、代码及在线练习地址:Python 实...

实验楼
07/16
0
0
Python实现文字聊天室

你是否想过用所学的Python开发一个图形界面的聊天室程序啊? 像这样的: image 如果你想开发这样一个有点怀旧风格的聊天程序,那么可以接着看; 要开发这个聊天程序,你需要具备以下知识点:...

实验楼
2017/12/01
0
0
Python让你自己做一个软件,自己开个聊天室,厉害吧!

今天分享之前先说下这个,,如果大家喜欢的话我会再更新,专注学习Python技术的小伙伴可以进群(474534951)一起交流学习,群里还有大量学习资料可供大家自行下载参看,欢迎大家一起来交流讨...

诸葛玥
2017/12/26
0
1
(总结)python 3程序开发指南(五)模块

1.有些情况下,使用单独的一条语句导入包中的所有模块会带来方便,为此在init.py文件中加入all=["",""]包含模块的名字。同理要想对导入模块中所有函数叶可以添加__all__选项,精确控制导入的...

索隆
2012/04/24
0
0
Python RPC 远程调用脚本之 RPyC 实践

最近有个监控需求,需要远程执行集群每个节点上的脚本,并获取脚本执行结果,为了安全起见不需要账号密码登陆节点主机,要求只需要调用远程脚本模块的方法就能实现。 总结下python进行远程调...

大数据之路
2015/06/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

WinDbg

参考来自:http://www.cnit.net.cn/?id=225 SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols ctrl + d to open dump_file Microsoft (R) Windows Debugger Version 6.12.0002.633......

xueyuse0012
30分钟前
2
0
OSChina 周五乱弹 —— 想不想把92年的萝莉退货

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @罗马的王:分享松澤由美的单曲《地球ぎ》 很久没看圣斗士星矢了 《地球ぎ》- 松澤由美 手机党少年们想听歌,请使劲儿戳(这里) @开源中国首...

小小编辑
57分钟前
10
1
springBoot条件配置

本篇介绍下,如何通过springboot的条件配置,控制Bean的创建 介绍下开发环境 JDK版本1.8 springboot版本是1.5.2 开发工具为 intellij idea(2018.2) 开发环境为 15款MacBook Pro 前言 很多时候,...

贺小五
今天
1
0
javascript source map 的使用

之前发现VS.NET会为压缩的js文添加一个与文件名同名的.map文件,一直没有搞懂他是用来做什么的,直接删除掉运行时浏览器又会报错,后来google了一直才真正搞懂了这个小小的map文件背后的巨大...

粒子数反转
昨天
1
0
谈谈如何学Linux和它在如今社会的影响

昨天,还在农耕脑力社会,今天已经人工智能技术、大数据、信息技术的科技社会了,高速开展并迅速浸透到当今科技社会的各个方面,Linux日益成为人们信息时代的到来,更加考验我们对信息的处理程...

linux-tao
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部