python asyncio data_received可以接收客户端发送的所有数据

时间:2017-08-09 02:41:45

标签: python python-3.x asynchronous python-asyncio

我正在学习python asyncio模块并尝试用它编写socks5服务器。 Python文档说:

  

收到某些数据时调用。 data是一个非空字节对象   包含传入的数据。

我想知道客户端何时发送2个字节数据,data_received(self, data)在调用时只接收1个字节而不是2个字节,其余1个字节将再次调用data_received(self, data)

#!/usr/bin/env python3

import asyncio
import logging
import socket
import struct

logging.basicConfig(level=logging.DEBUG,
                    format='{asctime} {levelname} {message}',
                    datefmt='%Y-%m-%d %H:%M:%S',
                    style='{')


class Remote(asyncio.Protocol):
    def connection_made(self, transport):
        self.transport = transport
        self.server_transport = None

    def data_received(self, data):
        self.server_transport.write(data)


class Server(asyncio.Protocol):
    INIT, REQUEST, REPLY = 0, 1, 2

    def connection_made(self, transport):
        client_info = transport.get_extra_info('peername')
        logging.info('connect from {}'.format(client_info))
        self.transport = transport
        self.state = self.INIT

    def data_received(self, data):
        if self.state == self.INIT:
            if data[0] == 5:
                amount = data[1]    # Authentication amount
                if 0 in data[2:]:
                    self.transport.write(b'\x05\x00')
                    self.state = self.REQUEST

                else:
                    self.eof_received()
            else:
                self.eof_received()

        elif self.state == self.REQUEST:
            ver, cmd, rsv, addr_type = data[:4]
            logging.info('addr type: {}'.format(addr_type))
            if addr_type == 1:    # ipv4
                addr = socket.inet_ntoa(data[4:8])

            elif addr_type == 3:
                addr_len = data[4]
                addr = data[5:5+addr_len]

            else:
                data = b'\x05\x08\x00\x01'
                data += socket.inet_aton('0.0.0.0') + struct.pack('>H', 0)
                self.transport.write(data)
                logging.error('not support addr type')
                self.eof_received()

            port = struct.unpack('>H', data[-2:])[0]
            logging.info('target: {}:{}'.format(addr, port))
            asyncio.ensure_future(self.remote(addr, port))
            self.state = self.REPLY

        elif self.state == self.REPLY:
            logging.info('start relay')
            self.remote_transport.write(data)

    async def remote(self, addr, port):
        loop = asyncio.get_event_loop()
        transport, _remote = await loop.create_connection(Remote, addr, port)
        _remote.server_transport = self.transport
        self.remote_transport = transport
        bind_addr, bind_port = transport.get_extra_info('sockname')
        data = b'\x05\x00\x00\x01'
        data += socket.inet_aton(bind_addr) + struct.pack('>H', bind_port)
        self.transport.write(data)


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    server = loop.create_server(Server, '127.0.0.2', 1089)
    loop.run_until_complete(server)

    try:
        loop.run_forever()

    except KeyboardInterrupt:
        server.close()
        loop.run_until_complete(server.close())
        loop.close()

1 个答案:

答案 0 :(得分:1)

不,data_received将接收服务器已接收的字节数。如果您需要接收前3个字节来处理请求,那么您应该在协议中实现一些缓冲,以允许您在继续之前等待请求的其余部分到达。

通常看起来像这样:

def __init__(self, …):
    self._buffer = bytearray()
    …

def data_received(self, data):
    self._buffer += data

    if self.state == self.INIT:
        # here we need at least 3 bytes.
        # if we don't have enough data yet, just wait for the next `data_received` call
        if len(self._buffer) < 3:
            return

        header, self._buffer = self._buffer[:2], self._buffer[2:]
        # parse authentication header, switch the state to REQUEST

    elif self.state == self.REQUEST:
        …