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内置数据类型详解
Aug 18 Python
Python使用PyCrypto实现AES加密功能示例
May 22 Python
使用python为mysql实现restful接口
Jan 05 Python
浅谈Python2获取中文文件名的编码问题
Jan 09 Python
对numpy中布尔型数组的处理方法详解
Apr 17 Python
对json字符串与python字符串的不同之处详解
Dec 19 Python
Python实现的KMeans聚类算法实例分析
Dec 29 Python
详解Python学习之安装pandas
Apr 16 Python
详解python中的模块及包导入
Aug 30 Python
Python 图像对比度增强的几种方法(小结)
Sep 25 Python
PyTorch在Windows环境搭建的方法步骤
May 12 Python
Python 字典中的所有方法及用法
Jun 10 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
多重?l件?合查?(二)
2006/10/09 PHP
ajax php传递和接收变量实现思路及代码
2012/12/19 PHP
完美解决thinkphp验证码出错无法显示的方法
2014/12/09 PHP
yii,CI,yaf框架+smarty模板使用方法
2015/12/29 PHP
PHP使用GD库制作验证码的方法(点击验证码或看不清会刷新验证码)
2017/08/15 PHP
Laravel中unique和exists验证规则的优化详解
2018/01/28 PHP
删除重复数据的算法
2006/11/23 Javascript
优秀js开源框架-jQuery使用手册(1)
2007/03/10 Javascript
HTML复选框和单选框 checkbox和radio事件介绍
2012/12/12 Javascript
火狐textarea输入法的bug的触发及解决
2013/07/24 Javascript
javascript 回到顶部效果的实现代码
2014/02/17 Javascript
jQuery实现定时读取分析xml文件的方法
2015/07/16 Javascript
跟我学习javascript的var预解析与函数声明提升
2015/11/16 Javascript
Bootstrap弹出带合法性检查的登录框实例代码【推荐】
2016/06/23 Javascript
AngularJS equal比较对象实例详解
2016/09/14 Javascript
使用Angular.js实现简单的购物车功能
2016/11/21 Javascript
关于Sequelize连接查询时inlude中model和association的区别详解
2017/02/27 Javascript
php输出全部gb2312编码内的汉字方法
2017/03/04 Javascript
自定义事件解决重复请求BUG的问题
2017/07/11 Javascript
荐书|您有一份JavaScript书单待签收
2017/07/21 Javascript
基于Vue2的独立构建与运行时构建的差别(详解)
2017/12/06 Javascript
vue项目中vue-i18n和element-ui国际化开发实现过程
2018/04/25 Javascript
NodeJs生成sitemap站点地图的方法示例
2019/06/11 NodeJs
原生js实现点击轮播切换图片
2020/02/11 Javascript
Node.js+Vue脚手架环境搭建的方法步骤
2020/03/08 Javascript
Python 3实战爬虫之爬取京东图书的图片详解
2017/10/09 Python
Python连接Hadoop数据中遇到的各种坑(汇总)
2020/04/14 Python
如何用Python和JS实现的Web SSH工具
2021/02/23 Python
html5定位并在百度地图上显示的示例
2014/04/27 HTML / CSS
携程英文网站:Trip.com
2017/02/07 全球购物
ghd法国官方网站:英国最受欢迎的美发工具品牌
2019/04/18 全球购物
应届生如何写自荐信
2014/01/05 职场文书
高校教师思想汇报
2014/01/11 职场文书
活动总结怎么写
2014/04/28 职场文书
2015年食品安全宣传周活动总结
2015/07/09 职场文书
2015年工商局个人工作总结
2015/07/23 职场文书