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实现Linux的find命令实例分享
Jun 04 Python
使用C++扩展Python的功能详解
Jan 12 Python
Python3.5.3下配置opencv3.2.0的操作方法
Apr 02 Python
聊聊python里如何用Borg pattern实现的单例模式
Jun 06 Python
django rest framework 实现用户登录认证详解
Jul 29 Python
Python Web框架之Django框架文件上传功能详解
Aug 16 Python
详解基于python的多张不同宽高图片拼接成大图
Sep 26 Python
python3中pip3安装出错,找不到SSL的解决方式
Dec 12 Python
Python3交互式shell ipython3安装及使用详解
Jul 11 Python
如何使用PyCharm引入需要使用的包的方法
Sep 22 Python
Python中BeautifulSoup通过查找Id获取元素信息
Dec 07 Python
Python实现照片卡通化
Dec 06 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下对数组进行排序的函数
2010/08/08 PHP
php 搜索框提示(自动完成)实例代码
2012/02/05 PHP
PHP通过插入mysql数据来实现多机互锁实例
2014/11/05 PHP
PHP添加文字水印或图片水印的水印类完整源代码与使用示例
2019/03/18 PHP
Yii框架操作cookie与session的方法实例详解
2019/09/04 PHP
IE/FireFox具备兼容性的拖动代码
2007/08/13 Javascript
jQuery 树形结构的选择器
2010/02/15 Javascript
jquery select动态加载选择(兼容各种浏览器)
2013/02/01 Javascript
图片延迟加载的实现代码(模仿懒惰)
2013/03/29 Javascript
微信小程序 用户数据解密详细介绍
2017/01/09 Javascript
AngularJS中的按需加载ocLazyLoad示例
2017/01/11 Javascript
vue.js实现价格格式化的方法
2017/05/23 Javascript
Angular4学习教程之DOM属性绑定详解
2018/01/04 Javascript
vue计算属性get和set用法示例
2019/02/08 Javascript
javascript实现鼠标点击生成文字特效
2019/12/24 Javascript
Element InfiniteScroll无限滚动的具体使用方法
2020/07/27 Javascript
ES11屡试不爽的新特性,你用上了几个
2020/10/21 Javascript
[03:41]2018完美盛典-《Fight With Us》
2018/12/16 DOTA
python基于右递归解决八皇后问题的方法
2015/05/25 Python
python实现稀疏矩阵示例代码
2017/06/09 Python
python3.4.3下逐行读入txt文本并去重的方法
2018/04/29 Python
python如何给字典的键对应的值为字典项的字典赋值
2019/07/05 Python
python异常处理try except过程解析
2020/02/03 Python
python3 正则表达式基础廖雪峰
2020/03/25 Python
matlab中二维插值函数interp2的使用详解
2020/04/22 Python
CSS3实现同时执行倾斜和旋转的动画效果
2016/10/27 HTML / CSS
HTML5事件方法全部汇总
2016/05/12 HTML / CSS
金蝶的一道SQL笔试题
2012/12/18 面试题
中药专业大学生医药工作求职信
2013/10/25 职场文书
财务专业大学生职业生涯规划范文
2013/12/30 职场文书
机关副主任个人四风问题整改措施
2014/09/26 职场文书
Nginx的rewrite模块详解
2021/03/31 Servers
修改MySQL的默认密码的四种小方法
2021/05/26 MySQL
Android开发手册Chip监听及ChipGroup监听
2022/06/10 Java/Android
SQL中的连接查询详解
2022/06/21 SQL Server
Go微服务项目配置文件的定义和读取示例详解
2022/06/21 Golang