博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在tornado中使用tcpserver和tcpclient实现echo服务器
阅读量:7193 次
发布时间:2019-06-29

本文共 5557 字,大约阅读时间需要 18 分钟。

本文主要介绍了在tornado框架中,使用tcpserver,tcpclient,struct.pack(),struct.unpack实现简单echo服务器的过程。

在网络通信中,需要发送二进制流数据;struct.pack()函数负责数据组包,即将数据按照规定的传输协议组合起来;struct.unpack()函数负责数据拆包,即按照规定的协议将数据拆分开来。

不多说,具体实现代码咱们来看一下。

tcp客户端代码如下:

# coding=utf-8import structimport loggingfrom tornado import ioloop, genfrom tornado.tcpclient import TCPClient"""tcpclient-struct.pack()组包发送数据包格式:消息头+消息体消息头:消息发送者(4字节)+消息接收者(4字节)+消息类型(1字节)+消息体中数据长度(4字节)消息体:待发送数据struct.unpack()拆包接收数据包格式:消息头+消息体消息头:消息发送者(4字节)+消息类型(1字节)+消息体中数据长度(4字节)消息体:待接收数据"""logging.basicConfig(level=logging.DEBUG)logger = logging.getLogger(__name__)class ChatClient(object):    def __init__(self, host, port):        self.host = host        self.port = port    @gen.coroutine    def start(self):        self.stream = yield TCPClient().connect(self.host, self.port)        while True:            yield self.send_message()            yield self.receive_message()    @gen.coroutine    def send_message(self):        # 待发送数据        msg = input("输入:")        bytes_msg = bytes(msg.encode("utf-8"))        # 消息发送者        chat_id = 10000000        # 消息接收者        receive_id = 10000001        # 消息类型 1-文本 2-图片 3-语音 4-视频 等        msg_type = 1        binary_msg = struct.pack("!IIBI"+str(len(msg))+"s", chat_id, receive_id, msg_type, len(msg), bytes_msg)        # 发送数据        yield self.stream.write(binary_msg)    @gen.coroutine    def receive_message(self):        """        接收数据        :return:        """        try:            logger.debug("receive data ...")            # 消息发送者 4字节            sender = yield self.stream.read_bytes(4, partial=True)            sender = struct.unpack('!I', sender)[0]            logger.debug("sender:%s", sender)            # 消息类型 1字节            msg_type = yield self.stream.read_bytes(1, partial=True)            msg_type = struct.unpack('!B', msg_type)[0]            logger.debug("msg_type:%s", msg_type)            # 消息长度 4字节            msg_len = yield self.stream.read_bytes(4, partial=True)            msg_len = struct.unpack('!I', msg_len)[0]            logger.debug("msg_len:%s", msg_len)            # 真实数据            data = yield self.stream.read_bytes(msg_len, partial=True)            data = struct.unpack("!" + str(msg_len) + "s", data)            logger.debug("data:%s", data)        except Exception as e:            logger.error("tcp client exception:%s", e)def main():    c1 = ChatClient("127.0.0.1", 8888)    c1.start()    ioloop.IOLoop.instance().start()if __name__ == '__main__':    main()

tcp服务端代码:

# coding=utf-8import structimport loggingfrom tornado.tcpserver import TCPServerfrom tornado.netutil import bind_socketsfrom tornado.iostream import StreamClosedErrorfrom tornado import genfrom tornado.ioloop import IOLoop"""tcpserver-struct.unpack()拆包接收数据包格式:消息头+消息体消息头:消息发送者(4字节)+消息接收者(4字节)+消息类型(1字节)+消息体中数据长度(4字节)消息体:待接收数据struct.pack()组包转发数据包格式:消息头+消息体消息头:消息发送者(4字节)+消息类型(1字节)+消息体中数据长度(4字节)消息体:待发送数据"""logging.basicConfig(level=logging.DEBUG)logger = logging.getLogger(__name__)class ChatServer(TCPServer):    PORT = 8888    clients = dict()    @gen.coroutine    def handle_stream(self, stream, address):        """        数据拆包并解析        :param stream:        :param address:        :return:        """        logger.debug("%s已上线", address)        ChatServer.clients[address] = stream        while True:            try:                # !表示使用大端方式解析数据                # 消息发送者 4字节                sender = yield stream.read_bytes(4, partial=True)                sender = struct.unpack('!I', sender)[0]                logger.debug("sender:%s", sender)                # 消息接收者 4字节                receiver = yield stream.read_bytes(4, partial=True)                receiver = struct.unpack('!I', receiver)[0]                logger.debug("receiver:%s", receiver)                # 消息类型 1字节                msg_type = yield stream.read_bytes(1, partial=True)                msg_type = struct.unpack('!B', msg_type)[0]                logger.debug("msg_type:%s", msg_type)                # 消息长度 4字节                msg_len = yield stream.read_bytes(4, partial=True)                msg_len = struct.unpack('!I', msg_len)[0]                logger.debug("msg_len:%s", msg_len)                if msg_type == 1:                    # 文本信息处理                    logger.debug("text message ...")                    self.handle_text_stream(stream, sender, msg_len)                elif msg_type == 2:                    logger.debug("picture message ...")                    self.handle_pic_stream(stream, sender, msg_len)            except StreamClosedError:                logger.debug("%s已下线", address)                del ChatServer.clients[address]                break    @gen.coroutine    def handle_text_stream(self, stream, sender, msg_len):        """        处理文本数据        :param stream:        :param send_to:        :param msg_len:        :return:        """        data = yield stream.read_bytes(msg_len, partial=True)        data = struct.unpack("!"+str(msg_len)+"s", data)        logger.debug("data:%s", data)        try:            # 打包数据,数据格式:数据发送者+数据类型+数据长度+数据体            binary_msg = struct.pack("!IBI" + str(msg_len) + "s", sender, 1, msg_len, data[0])            # 发送数据            yield stream.write(binary_msg)            logger.debug("="*25)        except KeyError:            # 将离线消息保存到数据库            pass    @gen.coroutine    def handle_pic_stream(self, stream, sender, msg_len):        passif __name__ == '__main__':    sockets = bind_sockets(ChatServer.PORT)    server = ChatServer()    server.add_sockets(sockets)    IOLoop.current().start()

以上就是具体的代码实现,如有错误,欢迎大家与我交流指正,谢谢!

转载地址:http://kzxkm.baihongyu.com/

你可能感兴趣的文章
mysql学习之路五(转)
查看>>
Beyond Compare比较表格小技巧
查看>>
第2章 理解面向对象
查看>>
数组的声明和遍历
查看>>
Mouse Key Hook
查看>>
Scrapy框架基础使用
查看>>
python学习笔记-(一)初识python
查看>>
前端的事件流以及事件处理程序
查看>>
react中create-react-app详情配置文档
查看>>
TLD单目标跟踪算法程序详解--OpenTLD Code 详解
查看>>
PDO基础知识
查看>>
汉诺塔问题(C++版)
查看>>
Basler和Matrox的配置及调试
查看>>
VirtualBOX 不能mount优盘,移动硬盘解决方案
查看>>
漫画:全面理解java.lang.IllegalArgumentException及其可用性设计
查看>>
[解题报告] 100 - The 3n + 1 problem
查看>>
SpringMVC HelloWorld实例开发及部署
查看>>
从最小割角度解决最大权闭合图问题及其在二分图形式下的优化
查看>>
sencha-touch2.0控件nestlist的简单用法
查看>>
python学习之控制语句
查看>>