解决问题:
1、bottle版本支持 ajax 请求处理:发现最新的13好像是可以的
2、websocket 复用 web 服务端口:
操作步骤:
1、将下边 test.py
test.html
两个文件放在一个目录下。
2、运行python test.py
3、浏览器打开地址 http://localhost:8080
4、打开浏览器调试工具。
数据
python test.py
import json
import time
from gevent import queue
from bottle import route, run, static_file, request, Bottle, abort
from gevent.pywsgi import WSGIServer
from geventwebsocket import WebSocketError
from geventwebsocket.handler import WebSocketHandler
class HTTP_APP(Bottle):
def __init__(self, user="admin", ip="127.0.0.1", port=8080):
"""
:param user:
:param ip: 服务地址
:param port: 服务端口
"""
Bottle.__init__(self)
self.ip = ip
self.port = port
# 测试websocket,添加信息字典
self._testUsers = {}
def __del__(self):
try:
if self.server != None:
self.server.close()
# self.server.close_server()
except:
pass
def start_server(self):
"""
启动websocket 服务
:return:
"""
self.server = WSGIServer(("0.0.0.0", self.port), self, handler_class=WebSocketHandler)
self.server.serve_forever()
def close_server(self):
if self.server != None:
self.server.close()
self.server = None
def register_http(self):
"""
web 服务注册
:return:
"""
@self.error(404)
def error404(error):
return u'404'
@self.route('/res/<resname:path>')
def res(resname):
"""
加载资源数据的路由
:param resname:
:return:
"""
return static_file(resname, root="./res")
@self.route('/')
def vote():
"""
主页面的路由
:return:
"""
return static_file("test.html", root="./")
# @self.hook('before_request')
# 网上说的要用到hoot方式
# def validate():
# """使用勾子处理页面或接口访问事件"""
# # 让bottle框架支持jquery ajax的RESTful风格的PUT和DELETE等请求
# """使用勾子处理页面或接口访问事件"""
# if request.method == 'POST' and request.POST.get('_method'):
# request.environ['REQUEST_METHOD'] = request.POST.get('_method', '').upper()
# if request.POST.get('_form'):
# request.environ['REQUEST_FORM'] = request.POST.get('_form', '')
#
# 处理ajax 请求。这个需要指定method
@self.route('/api/<apiname>', method='POST')
def asyncApi(apiname):
print(apiname)
# print(request.json)
a = request
# 读取通过表单提交的数据,我一般不用表单方式
# username = request.forms.get('username')
# password = request.forms.get('password')
# 读取post的参数,其他方式需要查看,a 的字典值。
postdata =request.body.readline()
req_data = json.loads(postdata)
print("request: ", req_data)
res = {
"code" : 0,
"msg" : "",
"data" : []
}
if req_data["action"]=="get":pass
if req_data['action']=="add":
self._testUsers[req_data['user']]={
"user" : req_data['user'],
"ip" : "10.10.10.10",
"port" :req_data['port'],
"stopDate" : req_data['stopDate'],
"status" : False
}
if req_data['action'] =="del":
del self._testUsers[req_data['user']]
if req_data['action'] == "start":
self._testUsers[req_data['user']]["status"] = True
if req_data['action'] == "stop":
self._testUsers[req_data['user']]["status"] = False
res["data"] = [ v for k,v in self._testUsers.items() ]
# 返回为序列化数据,否则报500错误
r = json.dumps(res)
print("response:", res)
return res
# websocket 服务
@self.route('/ws')
def handle_websocket():
# 页面打开一次,起一个请求
wsock = request.environ.get('wsgi.websocket')
# clientid = wsock.environ["REMOTE_ADDR"]
clientid = wsock.handler.client_address[0]
# print(clientid,clientid1)
# user_socket_dict[username] = user_socket
if not wsock:
abort(400, 'Expected WebSocket request.')
while True:
try:
message = wsock.receive()
print("Your message was: %r" % message)
wsock.send("Your message was: %r" % message)
except WebSocketError:
break
# https://blog.csdn.net/weixin_28938385/article/details/112903651
http_server = HTTP_APP()
http_server.register_http()
http_server.start_server()
# http_server.run()
http test.html
<!DOCTYPE html>
<html style="height: 100%">
<head>
<meta charset="utf-8">
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.0-alpha1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body style="height: 100%; margin: 0" onmousedown="whichButton(event)">
<div class="container" id="containerIdShow">
<div class="row">
<div class="col">
<p class="text-left">用户单位</p>
</div>
<div class="col">
<p class="text-left">端口</p>
</div>
<div class="col">
<p class="text-left">结束时间</p>
</div>
<div class="col">
<p class="text-left">状态</p>
</div>
<div class="col">
<p class="text-left">操作</p>
</div>
</div>
<div class="row">
<div class="col">
<input class="form-control" type="text" placeholder="" v-model="user" maxLength="64">
</div>
<div class="col">
<input class="form-control" type="number" placeholder="[8800-8900]" v-model="port" maxLength="4">
</div>
<div class="col">
<input class="form-control" type="date" placeholder="" v-model="stopDate" :min="dateXiaXian">
</div>
<div class="col">
</div>
<div class="col">
<button type="button" class="btn btn-outline-primary btn-sm" @click="addUser();console.log(123)" >添加</button>
</div>
</div>
<li class="list-group-item nav-item" >说明:web端php只能起一个进程,在linux环境下执行系统命令出于安全考虑不开放。请手动进入到目录启动</li>
<div class="row" v-for="(item,index) in userList">
<div class="col">
<p>
<span v-text='item.user'></span>
<a :href="'./index.html?ip='+item.ip+'&port='+item.port" target="_blank" class="btn btn-outline-warning btn-sm" >投屏</a>
<a :href="'./manage.html?ip='+item.ip+'&port='+item.port" target="_blank" class="btn btn-outline-warning btn-sm" >管理</a>
</p>
</div>
<div class="col">
<p><span v-text='item.port'></span></p>
</div>
<div class="col">
<p><span v-text='item.stopDate'></span></p>
</div>
<div class="col">
<p v-if="item.status">运行</p><p v-else>未运行</p>
</div>
<div class="col">
<button type="button" class="btn btn-outline-warning btn-sm" @click="exitUser(item.user, item.port)" >删除</button>
<button type="button" class="btn btn-outline-success btn-sm" :disabled="item.status" @click="startUser(item.user)" >启动</button>
<button type="button" class="btn btn-outline-secondary btn-sm" :disabled="!item.status" @click="stopUser(item.user, item.port)" >停止</button>
</div>
</div>
</div>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-gl/dist/echarts-gl.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-stat/dist/ecStat.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/extension/dataTool.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/map/js/china.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/map/js/world.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.0.0-alpha1/js/bootstrap.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/json2/20160511/json2.min.js"></script>
<script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script>
<script type="text/javascript">
var ws = new WebSocket("ws://"+window.location.host+"/ws");
ws.onopen = function() {
ws.send("Hello, world");
};
ws.onmessage = function (evt) {
console.log(evt.data);
};
function whichButton(event)
{
if (event.button==2) {
ws.send("你点击了鼠标右键!")
} else {
ws.send("你点击了鼠标左键!")
}
}
//
Date.prototype.Format = function(fmt)
{ //author: meizz
var o = {
"M+" : this.getMonth()+1, //月份
"d+" : this.getDate(), //日
"h+" : this.getHours(), //小时
"m+" : this.getMinutes(), //分
"s+" : this.getSeconds(), //秒
"q+" : Math.floor((this.getMonth()+3)/3), //季度
"S" : this.getMilliseconds() //毫秒
};
if(/(y+)/.test(fmt))
fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length));
for(var k in o)
if(new RegExp("("+ k +")").test(fmt))
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
return fmt;
}
const containerIdShow=new Vue({
el:"#containerIdShow",
data:{
user:"test",
port:"8888",
userList: [],
stopDate:"2022-01-01",
dateXiaXian:new Date().Format("yyyy-MM-dd"),
},
methods:{
startUser:function(user){
var _this=this;
var para={};
para.user = user;
para.action = "start";
jQuery.ajax({
url:"api/start",
async:true,
data:JSON.stringify(para),
dataType:"json",
type:"post",
success: function(result){
_this.userList=result.data;
//alert(result.msg);
}
});
},
stopUser:function(user, port){
var _this=this;
var para={};
para.user = user;
para.port = port;
para.action = "stop";
jQuery.ajax({
url:"api/stop",
async:true,
data:JSON.stringify(para),
dataType:"json",
type:"post",
success: function(result){
_this.userList=result.data;
//alert(result.msg);
}
});
},
exitUser:function(user, port){
var _this=this;
var para={};
para.user = user;
para.port = port;
para.action = "del";
jQuery.ajax({
url:"api/exit",
async:true,
data:JSON.stringify(para),
dataType:"json",
type:"post",
success: function(result){
_this.userList=result.data;
//alert(result.msg);
}
});
},
addUser:function(){
var _this=this;
var para={};
para.user = this.user;
para.port = this.port;
para.stopDate = this.stopDate;
para.action = "add";
console.log(para)
//参数检查/
//if(this.user.length==0){
// alert("用户不为空");
// return;
//}
//if(this.port>8900 || this.port<8800){
// alert("端口范围错误");
// return;
//}
//if(this.stopDate.length==0){
// alert("结束时间错误");
// return;
//}
//this.userList.forEach(item=>{
// if(item.user == para.user){
// alert("用户名已存在");
// this.user="";
// return;
// }
// if(item.port == para.port){
// alert("端口已使用");
// this.port=0;
// return;
// }
//});
///
jQuery.ajax({
url:"api/addUser",
//async:true,
data:JSON.stringify(para),
dataType:"json",
type:"post",
success: function(result){
_this.userList=result.data;
alert(result.msg);
}
});
this.user="";
this.port=0;
}
},
mounted: function(){
// 初始化子页面
var _this=this;
var para={};
para.action = "get";
jQuery.ajax({
url:"api/login",
//async:true,
data:JSON.stringify(para),
dataType:"json",
type:"post",
success: function(result){
console.log(result)
_this.userList=result.data;
}
});
},
created:function(){
}
});
</script>
</body>
</html>