Python Tornado实现WEB服务器Socket服务器共存并实现交互的方法
作者:John_Doe. 发布时间:2023-06-28 02:47:02
1、背景
最近有个项目,需要搭建一个socket服务器,一个web服务器,然后实现两个服务器之间的通讯交互。刚开始的方案是用Python中socket模块实现一个多线程的socket服务器,然后用Flask实现一个web服务器,他们之前通过线程交互实现通讯。
但是在我看来这个方案有例外一个更好的解决方法,就是用Torndao框架。鉴于网上用Tornado实现一个程序同时实现web服务和socket服务器并且实现交互的文章几乎没有,所以记录一下。觉得写得好麻烦点个赞,写得不好请指出,有疑问可以留言。
2、准备
2.1、环境部署
Python3.x
pip3 install Tornado
2.2、目录结构
目录结构如上图,这个目录结构包括文件命名只是我的个人习惯。其实目录结构不固定,只要合理就行。另外,原本项目是前后分离的只需要实现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都正常运行。
我在这里简单地写了一个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()
可以看到tornado异步的形式实现了多客户端同时接入socket。同时也可以测试web接口是正常的,如下图:
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、 效果
请求接口可以返回数据,已经成功发送一个客户端
客户端也能收到消息:
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、 效果
可以看到,从socket传过来的字符串,被我通过Api读取到了。
6、完整代码GitHub:https://github.com/JohnDoe1996/socket-web
来源:https://blog.csdn.net/sinat_34149445/article/details/106243103


猜你喜欢
- 前记在Python中, Dict是一系列由键和值配对组成的元素的集合, 它是一个可变容器模型,可以存储任意类型对象. Dict的存取速度非常
- php文件 <?php class xpathExtension{ public static function getNodes($
- eclipse安装Python插件之后,主要是为了方便Python代码就可以再Eclipse进行代码脚本,使用Eclipse开发Python
- 在 Go 语言中,struct 是一种常见的数据类型,它可以用来表示复杂的数据结构。在 struct 中,我们可以定义多个字段,每个字段可以
- Sklearn简介Scikit-learn(sklearn)是机器学习中常用的第三方模块,对常用的机器学习方法进行了封装,包括回归(Regr
- 一、思路1、通过window的aip函数CreateFile()函数获得文件句柄2、检测在获得句柄的时候是否报错“文件被占用无法打开”3、如
- 一个程序要进行交互,就需要进行输入,进行输入→处理→输出的过程。所以就需要用到输入和输出功能。同样的,在Python中,怎么实现输入和输出?
- 1.游戏画面1.1开始1.2射击怪物2.涉及知识点1.sprites2.pygame混音器3.图章 4.python
- 行高的概念看上去很简单——文字行的高度,其实,行高所涉及到的基础知识,对于今后理解其它属性也很重要。大片密密麻麻的文字往往会让人觉得乏味,因
- 网页制作中需要把握好很多原则和细节,今天我们来谈谈网页设计中的平衡、对比、连贯和留白。一、平衡如果你的页面是平衡的,当用户浏览这个页面的时候
- 本文实例为大家分享了Python3 Tkinkter + SQLite 实现登录和注册界面,供大家参考,具体内容如下Ubuntu14 + P
- 在 MySQL 中通常我们使用 limit 来完成页面上的分页功能,但是当数据量达到一个很大的值之后,越往后翻页,接口的响应速度就越慢。本文
- 游戏截图动态演示源码分享state/tool.pyimport osimport jsonfrom abc import abstractm
- 在学习和使用各种数据库的过程中,我们常常会遇到聚族索引、非聚族索引、组合索引的概念,这些索引对我们使用数据库,特别是查询的速度的
- 上节我们了解了图形验证码的识别,简单的图形验证码我们可以直接利用 Tesserocr 来识别,但是近几年又出现了一些新型验证码,如滑动验证码
- 先放关键代码:i = tf.train.range_input_producer(NUM_EXPOCHES, num_epochs=1, s
- 日常小程序经常需要分页查询的功能,本篇我们讲解一下低代码中如何实现分页查询的功能。要自己开发分页功能,可以先参考官方的方法分页查询我们一般是
- 一、前言容器使用沙箱机制,互相隔离,优势在于让各个部署在容器的里的应用互不影响,独立运行,提供更高的安全性。本文主要介绍python应用(d
- 大家好,本文将分享如何使用matplotlib制作动态条形图,制作的图很美,这个是我在之前发布的一篇中使用的图片,效果如下制作思路为了方便大
- 将无权点文件转化成邻接矩阵目前点文件是两列Excel代码,在进行复杂网络运算时需要转化成邻接矩阵。我在网上找了一个代码,稍微修改了下,亲测可