场景描述:
在做大屏展示时,有这样一个需求:在不刷新页面的情况下,动态改变大屏展示内容,如:执行某个函数,把相关数据醒目展示,轮换数据显示顺序等等。比如有领导参观时,马上切换到领导感兴趣的页面(面向领导编程^_^)
实现思路:
1.搭建一个Websocket服务器。
2.让大屏与Websocket服务器建立长连接。(此操作存在风险,请斟酌使用)
3.再写一个控制页面,用来控制大屏展示内容(css,js)。
实现步骤:
1.搭建一个Websocket服务器,这里使用Python的websockets来搭建。话不多说,直接上代码。
import asyncio
import websockets
import time
import json
host = '0.0.0.0'
port=8765
client_list = {} #客户端列表
# 检测客户端权限,用户名密码通过才能退出循环
async def check_permit(websocket):
while True:
recv_str = await websocket.recv()
cred_dict = recv_str.split(":")
if (cred_dict[0] == "screen" and cred_dict[1] == "123456") or (cred_dict[0] == "screencontrol" and cred_dict[1] == "123456"):
response_str = "{\"action\":\"login_result\",\"data\":\"登录成功\"}"
await websocket.send(response_str)
return True #关闭这个socket
else:
response_str ="{\"action\":\"login_result\",\"data\":\"抱歉,用户名或密码错误\"}"
await websocket.send(response_str) #挂起,不进入下面代码
# 接收客户端消息并处理,这里只是简单把客户端发来的返回回去
async def recv_msg(websocket):
addr=str(websocket.remote_address)
client_list[addr]=websocket#用addr作为客户端标识
print(addr, "上线了,当前在线:", len(client_list))
while True:
try:
#接收信息
recv_text = await websocket.recv()
print(addr+" say:"+ recv_text)
response=""
#解析传入json
o = json.loads(recv_text)
action=o.get('action')
data=o.get('data')
if len(action) > 0:
if (action=="1"):
response = data
elif (action=="2"):
response = data
else:
response = recv_text
#通知其他客户端
for key,socket in client_list.items():
if(key!=addr):
await socket.send(response)
# #返回信息
# response_text = f"server: {recv_text}"
# #await websocket.send(response_text)
# #通知其他客户端
# for key,socket in client_list.items():
# await socket.send(response_text)
except Exception as error:
print("exception:", error)
if addr in client_list:
client_list.pop(addr) #从客户端列表移除
print("当前在线:", len(client_list))
return True
# 服务器端主逻辑
async def main(websocket, path):
try:
await check_permit(websocket)
await recv_msg(websocket)
except Exception as error:
print("Exception:", error)
start_server = websockets.serve(main, host, port)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
这里有个简单授权检查,当客户端连到Websocket服务器时,先要发送证明自己身份的消息,服务器端进行验证,通过了,才能进行后面的交互,否则直接拦截在外面。
2.大屏展示页面,此页面用于模拟大屏展示的效果。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>大屏演示</title>
<script src="https://www.rc114.com/js/jquery-1.11.3.min.js"></script>
<style type="text/css">
input {
padding: 3px;
outline: none;
border: 1px solid #ccc;
}
.log {
padding: 10px 0;
color: gray;
font-size: 14px;
line-height: 25px;
}
.main {
margin: 40px auto;
width: 80%;
}
.showArea {
text-align: center;
font-size: 48px;
padding: 150px 0;
}
.title {
border-bottom: 2px solid brown;
padding-bottom: 10px;
}
</style>
</head>
<body>
<div class="main">
<h3 class="title">大屏演示</h3>
<div class="showArea">
大屏演示区域
</div>
<span style="font-size: 14px;color:brown;">此处打印数据交互日志,可以根据实际情况删除或隐藏。</span>
<div class="log"></div>
</div>
<script type="text/javascript">
var ws;
var server = "192.168.100.192";
var port = "8765";
$(function () {
conn();
})
function onmessage(msg) {
log(msg);
//处理服务器返回消息
var o = JSON.parse(msg);
var action = o.action;
var data = o.data;
switch (action) {
case "login_result":
break;
case "setcss":
createStyleSheet(data);
break;
case "runjs":
eval(data)
break;
default:
break;
}
}
function conn() {
ws = new WebSocket("ws://" + server + ":" + port);
ws.onopen = function () {
ws.send("screen:123456");
log("连接成功");
};
ws.onmessage = function (evt) {
onmessage(evt.data);
};
ws.onclose = function () {
log("连接已关闭...");
};
}
function log(msg) {
$(".log").append(getNowTime() + ":" + msg + "</br>");
}
function getNowTime() {
var date = new Date();
var year = date.getFullYear();
var month = date.getMonth() + 1;
var day = date.getDate();
var hour = date.getHours();
var minute = date.getMinutes();
var second = date.getSeconds();
var time = year + '-' + addZero(month) + '-' + addZero(day) + ' ' + addZero(hour) + ':' + addZero(minute) + ':' + addZero(second);
return time;
}
function addZero(s) {
return s < 10 ? ('0' + s) : s;
}
function createStyleSheet(css) {
var dynamic = document.getElementById("_dynamicCss");
if (dynamic == undefined) {
dynamic = document.createElement('style');
dynamic.id = "_dynamicCss";
var head = document.head || document.getElementsByTagName('head')[0];
head.appendChild(dynamic);
}
dynamic.type = 'text/css';
dynamic.innerText = css;
}
</script>
</div>
</body>
</html>
(1)页面加载完毕后,与Websocket服务器建立连接。
(2)在ws.onmessage事件处理服务器端返回的指令,此处写了3个事件类型login_result,setcss,runjs,login_result用来处理登录回调,setcss设置页面css样式,runjs用来执行js代码。
3.大屏控制页,用来控制大屏显示效果。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>大屏控制端</title>
<script src="https://www.rc114.com/js/jquery-1.11.3.min.js"></script>
<style type="text/css">
* {
font-size: 14px;
}
h3 {
font-size: 18px;
}
.main {
margin: 40px auto;
width: 80%;
}
.main table {
border: 1px solid #ccc;
border-collapse: collapse;
width: 100%;
}
.main .td-left {
text-align: right;
width: 100px;
}
.main table td {
padding: 5px;
border: 1px solid #ccc;
}
input,
select,
textarea {
padding: 3px;
outline: none;
border: 1px solid #ccc;
}
.log {
padding: 10px 0;
color: gray;
font-size: 14px;
line-height: 25px;
}
input[type="button"] {
background-color: brown;
padding: 3px 5px;
color: white;
border: 0;
}
</style>
</head>
<body>
<div class="main">
<h3 style="border-bottom: 2px solid brown;padding-bottom: 10px;">大屏控制端</h3>
<table>
<tr>
<td colspan="2" style="text-align: center;line-height: 32px;">
用此页面可以控制大屏页面的展示效果,可以追加css,可以执行js方法,实时生效。
</td>
</tr>
<tr>
<td class="td-left">服务器</td>
<td>
<span class="server"></span>
<input type="button" value="连接" id="btnCon">
</td>
</tr>
<tr>
<td class="td-left">操作</td>
<td>
<select class="action">
<option value="setcss">设置css样式(setcss)</option>
<option value="runjs">执行JS方法(runjs)</option>
</select>
</td>
</tr>
<tr>
<td class="td-left">内容</td>
<td>
<textarea style="width: 99%;height: 200px;" class="data">body{background-color:#e3e3e3}</textarea>
</td>
</tr>
<tr>
<td class="td-left"> </td>
<td>
<input type="button" value="发送消息" id="btnSend">
<input type="button" value="清除日志" id="btnClearLog">
</td>
</tr>
<tr>
<td class="td-left">日志</td>
<td>
<div class="log"></div>
</td>
</tr>
</table>
</div>
<script type="text/javascript">
var ws;
var server = "192.168.100.192";
var port = "8765";
$(function () {
$(".server").text("ws://" + server + ":" + port);
conn();
$("#btnCon").click(function () {
conn();
});
$("#btnSend").click(function () {
if (ws.readyState == 1) {
var action = $(".action").val();
var data = $(".data").val();
if (action.length > 0) {
var o = {};
o["action"] = action;
o["data"] = data;
ws.send(JSON.stringify(o));
}
}
});
$("#btnClearLog").click(function () {
$(".log").text("");
});
})
function onmessage(msg) {
log(msg);
}
function conn() {
ws = new WebSocket("ws://" + server + ":" + port);
ws.onopen = function () {
ws.send("screencontrol:123456");
log("连接成功");
$(".server").html("ws://" + server + ":" + port + "<span style='color:green'> 连接成功</span>")
};
ws.onmessage = function (evt) {
onmessage(evt.data);
};
ws.onclose = function () {
log("连接断开(event.code):" + event.code);
$(".server").html("ws://" + server + ":" + port + "<span style='color:red'> 连接断开</span>")
};
}
function log(msg) {
$(".log").append(getNowTime() + ":" + msg + "</br>");
}
function getNowTime() {
var date = new Date();
var year = date.getFullYear();
var month = date.getMonth() + 1;
var day = date.getDate();
var hour = date.getHours();
var minute = date.getMinutes();
var second = date.getSeconds();
var time = year + '-' + addZero(month) + '-' + addZero(day) + ' ' + addZero(hour) + ':' + addZero(minute) + ':' + addZero(second);
return time;
}
function addZero(s) {
return s < 10 ? ('0' + s) : s;
}
</script>
</div>
</body>
</html>
运行效果:
在大屏控制页,输入指定命令,就可以动态改版大屏展示页的效果了。
大屏连Websocket存在风险,如果Websocket服务器被黑了,就可能会显示其他内容,请谨慎使用。