Python Tornado实现WEB服务器Socket服务器共存并实现交互的方法


Posted in Python onMay 26, 2020

1、背景

最近有个项目,需要搭建一个socket服务器,一个web服务器,然后实现两个服务器之间的通讯交互。刚开始的方案是用Python中socket模块实现一个多线程的socket服务器,然后用Flask实现一个web服务器,他们之前通过线程交互实现通讯。
但是在我看来这个方案有例外一个更好的解决方法,就是用Torndao框架。鉴于网上用Tornado实现一个程序同时实现web服务和socket服务器并且实现交互的文章几乎没有,所以记录一下。觉得写得好麻烦点个赞,写得不好请指出,有疑问可以留言。

2、准备

2.1、环境部署

  • Python3.x
  • pip3 install Tornado

2.2、目录结构

Python Tornado实现WEB服务器Socket服务器共存并实现交互的方法

目录结构如上图,这个目录结构包括文件命名只是我的个人习惯。其实目录结构不固定,只要合理就行。另外,原本项目是前后分离的只需要实现API接口,所以我这里就没有涉及到HTML的东西。

3、服务器的实现

3.1、Socket服务器实现

socket服务器部分实现主要靠 Tornado中的TCPServer类

3.1.1、 导入类

socket_server.py:

from tornado.iostream import IOStream  # 这句可以没有,只是作为参数的代码提示
from tornado.tcpserver import TCPServer

3.1.2、 构建一个Connecter类

socket_server.py:

class Connecter:

  clients = set()   # 存放连接的客户端

  async def init(self, stream: IOStream, address: tuple):
    """
    注意这个不是构造方法,这里不用构造方法是为了方便后续的与web端相互通信
    """
    self.stream, self.address = stream, address
    self.clients.add(self)
    print("{address} 上线!".format(address=address))
    self.stream.set_close_callback(self.onClose) # 客户端离线的时候会被调用
    await self.receive()    # 接受消息

  async def receive(self):
    """
    接受消息
    """
    while True:
      try:  # 因为异步的原因。可能设备离线后还在接收消息,所以try一下,不让错误打印出来,其实打印了错误也不影响程序运行
        data = await self.stream.read_bytes(num_bytes=1024, partial=True)  # num_bytes:每次最多接收字节,partial:数据中断后视为接收完成
        print(data)
        # TODO:接收到数据的处理
      except: 
        pass

  def send(self, data):
    """
    发送消息
    :param data: 消息内容
    """
    self.stream.write(bytes(data.encode('utf8')))

  def onClose(self):
    """
    客户端离线
    """
    print("{address} 离线!".format(address=self.address))
    self.clients.remove(self) # 在clients内删掉该客户端

3.1.3、 构建一个SocketServer类

socket_server.py:

class SocketServer(TCPServer):   # 需要继承TCPServer这个类
  async def handle_stream(self, stream: IOStream, address: tuple):  # 实现类里面的handle_stream方法
    await Connecter().init(stream, address)   # 每次有客户端连入都实例化一个Connecter类

3.2、Web服务器实现

3.2.1、 实现一个requestHandler

web_test.py:

from tornado.web import RequestHandler   # 导入RequestHandler类


class TestApiHandler(RequestHandler):    # 继承RequestHandler类

  def get(self):   # 实现GET方法,GET请求会执行这个方法
    pass

  def post(self):   # 实现POST方法,POST请求会执行这个方法
    pass

3.2.2、 实现web app

web_server.py:

from tornado.web import Application     # 导入Tornado的Application类
from .src.web_test import TestApiHandler  # 导入我们自己写的TestApiHandler类


def webServerApp():   # 构造出webApp
  return Application([
    (r'/api_test/', TestApiHandler),  # 把/api_test/路由到TestApiHandler
  ])

3.3、程序入口

3.3.1、 导入web_server和socket_server,还有导入tornado的ioloop

main.py:

from web_server.web_server import webServerApp
from socket_server.socket_server import SocketServer
from tornado import ioloop
from tornado.options import define, options

3.3.2、 定义默认端口

main.py

#这里用define定义端口,可以方便使用命令行参数的形式修改端口
define("socketPort", 8888, type=int)  # socket默认使用8888端口
define("webPort", 8080, type=int)    # web默认使用8080端口

3.3.3、 启动代码

main.py

def main():
  socket_server = SocketServer()
  socket_server.listen(options.socketPort, '0.0.0.0')
  print("socket服务器启动,端口:{port}".format(port=options.socketPort))
  app = webServerApp()
  app.listen(options.webPort, '0.0.0.0')
  print("web服务器启动,端口:{port}".format(port=options.webPort))
  ioloop.IOLoop.current().start()


if __name__ == '__main__':
  main()

4、服务器运行效果

到此,一个混合型的socket+web服务器已经搭建好了。我们我们运行main.py文件可以看到打印的信息,socket和web都正常运行。

Python Tornado实现WEB服务器Socket服务器共存并实现交互的方法

我在这里简单地写了一个socket客户端测试,代码如下:

import socket
import datetime


class Client:

  def __init__(self):
    with socket.create_connection(("127.0.0.1", 8888)) as sock:
      while True:
        msg = sock.recv(1024)
        if len(msg) > 0:
          print(msg)
          sock.send(bytes(str(datetime.datetime.now).encode('utf8')))
        msg = []
            
        

if __name__ == "__main__":
  Client()

Python Tornado实现WEB服务器Socket服务器共存并实现交互的方法

可以看到tornado异步的形式实现了多客户端同时接入socket。同时也可以测试web接口是正常的,如下图:

Python Tornado实现WEB服务器Socket服务器共存并实现交互的方法

5、Web服务器与Socket服务器交互

重点来了,web和socket怎样实现交互呢?其实很简单。

5.2、 web >> socket

web_test.py -> TestApiHandler -> post:

5.2.1、 导入Connecter类

from socket_server.socket_server import Connecter

5.2.2、 实现请求接口发送消息到socket客户端

def post(self):   # 实现POST方法,POST请求会执行这个方法
    msg = self.get_argument("msg") # 得到post请求中的msg的值
    ip = self.get_argument('ip')  # 得到要发送的ip
    c = Connecter()   # 实例化Connecter类
    counter = 0   # 记录发送到客户端的个数
    for client in c.clients:  # type:Connecter
      if client.address[0] == ip:   # 根据ip发送
        client.send(msg)  # 发送消息
        counter += 1    # 计数加1
    self.write("{'send_counter':" + str(counter) + "}")

5.2.3、 效果

请求接口可以返回数据,已经成功发送一个客户端

Python Tornado实现WEB服务器Socket服务器共存并实现交互的方法

客户端也能收到消息:

Python Tornado实现WEB服务器Socket服务器共存并实现交互的方法

5.1、 socket >> web

其实socket发送的消息让web马上收到消息是不太现实的,但是我们可以把数据保存起来(可以是数据库、全局变量、缓存……),然后通过api接口再把数据取出。另外还有一种方法是通过socket和websocket进行交互通讯,这种方法是推荐的方法,同样的也可以用Tornado去实现,感兴趣可以去研究一下也很简单。如何有需要我提供socket、websocket、web三个端都互相交互的例子可以留言。
这里为了简单一点,我使用一个类作为全局变量来保存数据,然后用接口访问,拿出这个类的值来演示一下效果。

5.1.1、 声明类作为全局变量

socket_data_processing.py

class SocketData:
  msg = ""

5.1.2、 接受到的消息保存到这个类里面的msg

socket_server.py -> Connecter -> receive

async def receive(self):
    """
    接受消息
    """
    while True:
      try:  # 因为异步的原因。可能设备离线后还在接收消息,所以try一下,不让错误打印出来,其实打印了错误也不影响程序运行
        data = await self.stream.read_bytes(num_bytes=1024, partial=True)  # num_bytes:每次最多接收字节,partial:数据中断后视为接收完成
        print(data)
        from .src.socket_data_processing import SocketData
        SocketData.msg = data.decode('utf8')
      except:
        pass

5.1.3、 用get方法显示socket显示回来的数据

web_test.py -> TestApiHandler -> get:

def get(self):   # 实现GET方法,GET请求会执行这个方法
    from socket_server.src.socket_data_processing import SocketData
    self.write(SocketData.msg)

5.1.4、 效果

Python Tornado实现WEB服务器Socket服务器共存并实现交互的方法

可以看到,从socket传过来的字符串,被我通过Api读取到了。

6、完整代码GitHub:https://github.com/JohnDoe1996/socket-web

到此这篇关于Python Tornado实现WEB服务器Socket服务器共存并实现交互的方法的文章就介绍到这了,更多相关Python  WEB服务器Socket服务器共存交互内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python中的map、reduce和filter浅析
Apr 26 Python
朴素贝叶斯算法的python实现方法
Nov 18 Python
Python实现读写sqlite3数据库并将统计数据写入Excel的方法示例
Aug 07 Python
pandas 把数据写入txt文件每行固定写入一定数量的值方法
Dec 28 Python
Python人工智能之路 之PyAudio 实现录音 自动化交互实现问答
Aug 13 Python
Pyspark获取并处理RDD数据代码实例
Mar 27 Python
Python %r和%s区别代码实例解析
Apr 03 Python
解决paramiko执行命令超时的问题
Apr 16 Python
如何安装并在pycharm使用selenium的方法
Apr 30 Python
浅析Python 责任链设计模式
Sep 11 Python
python使用smtplib模块发送邮件
Dec 17 Python
python基础学习之生成器与文件系统知识总结
May 25 Python
tensorflow实现从.ckpt文件中读取任意变量
May 26 #Python
打印tensorflow恢复模型中所有变量与操作节点方式
May 26 #Python
tensorflow模型的save与restore,及checkpoint中读取变量方式
May 26 #Python
tensorflow从ckpt和从.pb文件读取变量的值方式
May 26 #Python
Pytorch转keras的有效方法,以FlowNet为例讲解
May 26 #Python
Django+Celery实现动态配置定时任务的方法示例
May 26 #Python
python删除某个目录文件夹的方法
May 26 #Python
You might like
PHP 和 MySQL 基础教程(四)
2006/10/09 PHP
一个没有MYSQL数据库支持的简易留言本的编写
2006/10/09 PHP
php 无法载入mysql扩展
2010/03/12 PHP
PHP设计模式之调解者模式的深入解析
2013/06/13 PHP
php递归遍历删除文件的方法
2015/04/17 PHP
在WordPress中实现发送http请求的相关函数解析
2015/12/29 PHP
深入浅析PHP的session反序列化漏洞问题
2017/06/15 PHP
PHP堆栈调试操作简单示例
2018/06/15 PHP
让你的PHP,APACHE,NGINX支持大文件上传
2021/03/09 PHP
摘自启点的main.js
2008/04/20 Javascript
JS自动缩小超出大小的图片
2012/10/12 Javascript
js hover 定时器(实例代码)
2013/11/12 Javascript
jQuery获取选中内容及设置元素属性的方法
2014/07/09 Javascript
jQuery Easyui快速入门教程
2016/08/21 Javascript
javascript简易画板开发
2020/04/12 Javascript
Nodejs进阶:express+session实现简易登录身份认证
2017/04/24 NodeJs
vue2.0基于vue-cli+element-ui制作树形treeTable
2019/04/30 Javascript
AngularJs中$cookies简单用法分析
2019/05/30 Javascript
Vue编程式跳转的实例代码详解
2019/07/10 Javascript
js实现简单五子棋游戏
2020/05/28 Javascript
[02:19]DOTA2上海特级锦标赛 观赛指南 Spectator Guide
2016/02/04 DOTA
[33:17]OG vs VGJ.T 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
Python获取系统默认字符编码的方法
2015/06/04 Python
Python求算数平方根和约数的方法汇总
2016/03/09 Python
使用python制作游戏下载进度条的代码(程序说明见注释)
2019/10/24 Python
Pandas将列表(List)转换为数据框(Dataframe)
2020/04/24 Python
python爬取豆瓣电影排行榜(requests)的示例代码
2021/02/18 Python
Python3压缩和解压缩实现代码
2021/03/01 Python
html5新特性与用法大全
2018/09/13 HTML / CSS
英国最大的专业户外零售商:Mountain Warehouse
2018/06/06 全球购物
优秀班组长事迹
2014/05/31 职场文书
地球一小时宣传标语
2014/06/24 职场文书
民族学专业大学生职业规划范文:清晰未来的构想
2014/09/20 职场文书
孕妇离婚协议书范本
2014/11/20 职场文书
2014年内部审计工作总结
2014/12/09 职场文书
使用refresh_token实现无感刷新页面
2022/04/26 Javascript