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实现手写一个类似django的web框架示例
Jul 20 Python
实例分析python3实现并发访问水平切分表
Sep 29 Python
Tensorflow实现酸奶销量预测分析
Jul 19 Python
python实现证件照换底功能
Aug 20 Python
Python API自动化框架总结
Nov 12 Python
Python高级特性——详解多维数组切片(Slice)
Nov 26 Python
django之从html页面表单获取输入的数据实例
Mar 16 Python
Django自关联实现多级联动查询实例
May 19 Python
django中cookiecutter的使用教程
Dec 03 Python
一篇文章搞懂python混乱的切换操作与优雅的推导式
Aug 23 Python
Python实现滑雪小游戏
Sep 25 Python
python语言中pandas字符串分割str.split()函数
Aug 05 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
全国FM电台频率大全 - 31 新疆维吾尔族自治区
2020/03/11 无线电
PHP 网络开发详解之远程文件包含漏洞
2010/04/25 PHP
PHP对MongoDB[NoSQL]数据库的操作
2013/03/01 PHP
PHP版微信第三方实现一键登录及获取用户信息的方法
2016/10/14 PHP
PHP7 mongoDB扩展使用的方法分享
2019/05/02 PHP
js一组验证函数
2008/12/20 Javascript
jQuery live
2009/05/15 Javascript
JS判断不能为空实例代码
2013/11/26 Javascript
浅析js中的浮点型运算问题
2014/01/06 Javascript
Jquery选择器中使用变量实现动态选择例子
2014/07/25 Javascript
jquery基础知识第一讲之认识jquery
2016/03/17 Javascript
jquery实现界面无刷新加载登陆注册
2016/07/30 Javascript
jQuery的extend方法【三种】
2016/12/14 Javascript
js for循环倒序输出数组元素的实例
2017/03/01 Javascript
vue.js动态数据绑定学习笔记
2017/05/19 Javascript
如何用webpack4带你实现一个vue的打包的项目
2018/06/20 Javascript
Vue实现底部侧边工具栏的实例代码
2018/09/03 Javascript
详解微信小程序与内嵌网页交互实现支付功能
2018/10/22 Javascript
element-ui组件中input等的change事件中传递自定义参数
2019/05/22 Javascript
ES6 let和const定义变量与常量的应用实例分析
2019/06/27 Javascript
layui自定义验证,用ajax查询后台是否有重复数据,form.verify的例子
2019/09/06 Javascript
javascript实现京东登录显示隐藏密码
2020/08/02 Javascript
vant组件中 dialog的确认按钮的回调事件操作
2020/11/04 Javascript
Python wxPython库消息对话框MessageDialog用法示例
2018/09/03 Python
python实现推箱子游戏
2020/03/25 Python
Python从列表推导到zip()函数的5种技巧总结
2019/10/23 Python
自定义Django Form中choicefield下拉菜单选取数据库内容实例
2020/03/13 Python
python使用re模块爬取豆瓣Top250电影
2020/10/20 Python
Funko官方商店:源自美国,畅销全球搪胶收藏玩偶
2018/09/15 全球购物
社团成立邀请函
2014/01/08 职场文书
保护野生动物倡议书
2014/05/16 职场文书
幼儿园教师管理制度
2015/08/05 职场文书
mysql部分操作
2021/04/05 MySQL
pandas中DataFrame重置索引的几种方法
2021/05/24 Python
CSS中Single Div 绘图技巧的实现
2021/06/18 HTML / CSS
java实现web实时消息推送的七种方案
2022/07/23 Java/Android