目录

1.种植园使用websocket代替http

2.服务端基于socket提供服务

3.服务端响应信息

4.种植园页面展示

1.种植园使用websocket代替http

我们需要完成的种植园,是一个互动频繁,并且要求有一定即时性的模块,所以如果继续基于http协议开发,那么需要通过ajax发送大量http请求,同时因为http本身属于单向通讯,所以服务端无法主动发送信息提供给客户端。所以对于客户端使用来说,非常不友好,所以我们需要基于socket通讯来完成这个模块的开发。当然,如果我们服务端基于socket实现tcp/ip通讯的同时,那么客户端必须也要使用websocket来实现tcp/ip通讯才能正常运作。

1.websocket协议简介

文档:https://tools.ietf.org/html/rfc6455

一直以来,HTTP是无状态、单向通信的网络协议,即客户端请求一次,服务器回复一次,默认情况下,只允许浏览器向服务器发出请求后,服务器才能返回相应的数据。如果想让服务器消息及时下发到客户端,需要采用类似于轮询的机制,大部分情况就是客户端通过定时器使用ajax频繁地向服务器发出请求。这样的做法效率很低,而且HTTP数据包头本身的字节量较大,浪费了大量带宽和服务器资源。

为了提高效率,HTML5推出了WebSocket技术。

WebScoket是一种让客户端和服务器之间能进行全双工通信(full-duplex)的技术。它是HTML最新标准HTML5的一个协议规范,本质上是个基于TCP的协议,它通过HTTP/HTTPS协议发送一条特殊的请求进行握手后创建了一个TCP连接,此后浏览器/客户端和服务器之间便可随时随地以通过此连接来进行双向实时通信,且交换的数据包头信息量很小。

同时为了方便使用,HTML5提供了非常简单的操作就可以让前端开发者直接实现socket通讯,开发者只需要在支持WebSocket的浏览器中,创建Socket之后,通过onopen、onmessage、onclose、onerror四个事件的实现即可处理Socket的响应。

注意:websocket是HTML5技术的一部分,但是websocket并非只能在浏览器或者HTML文档中才能使用,事实上在python或者C++等语言中只要能实现websocket协议报文,均可使用。

导读:https://blog.csdn.net/zhusongziye/article/details/80316127

客户端报文

GET /mofang/websocket HTTP/1.1
Host: 127.0.0.1
Origin: http://127.0.0.1:5000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ== # Sec-WebSocket-Key 是随机生成的
Sec-WebSocket-Version: 13

服务端报文

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= # 结合客户端提供的Sec-WebSocket-Key基于固定算法计算出来的
Sec-WebSocket-Protocol: chat

2.WebSocket与Socket的关系

他们两的关系就像Java和JavaScript,并非完全没有关系,只能说有点渊源。

Socket严格来说,其实并不是一个协议,而是为了方便开发者使用TCP或UDP协议而对TCP/IP协议进行封装出来的一组接口,是位于应用层和传输控制层之间的接口。通过Socket接口,我们可以更简单,更方便的使用TCP/IP协议。

WebSocket是实现了浏览器与服务器的全双工通信协议,一个模拟Socket的应用层协议。

2.服务端基于socket提供服务

1.安装并配置flask-socketio

在python中实现socket服务端的方式有非常多,一种最常用的有python-socketio,而我们现在使用的flask框架也有一个基于python-socket模块进行了封装的flask-socketio模块.

官方文档:https://flask-socketio.readthedocs.io/en/latest/

注意:

因为目前还有会存在一小部分的设备或者应用是不支持websocket的.所以为了保证功能的可用性,我们使用socektio,但是由此带来了2个问题,必须要注意的:

  1. python服务端使用基于socketio进行通信服务,则另一端必须也是基于socetio来进行对接通信,否则无法进行通信

  2. socketio还有一个版本对应的问题, 版本不对应则无法通信.回报版本错误.

    如果使用了javascript io 1.x或者2.x版本,则python-socketio或者flask-socketio的版本必须是4.x

    如果使用了javascriptio 3.x版本,则python-socketio或者flask-socketio的版本必须是5.x.

我们当前使用的flask-socketio版本是5.x,所以javasctipt的socketio版本就必须是3.x.

终端下执行命令,安装:

pip install flask-socketio
pip install gevent-websocket

模块初始化,application/__init__.py,代码:

from flask_socketio import SocketIO

# socketio
socketio = SocketIO() def init_app(config_path):
"""全局初始化""" # socketio
socketio.init_app(app, cors_allowed_origins=app.config["CORS_ALLOWED_ORIGINS"],async_mode=app.config["ASYNC_MODE"], debug=app.config["DEBUG"])
# 改写runserver命令
if sys.argv[1] == "runserver":
manager.add_command("run", socketio.run(app,host=app.config["HOST"],port=app.config["PORT"]))

配置文件,application/settings/dev.py,代码:

    # socketio
CORS_ALLOWED_ORIGINS="*"
ASYNC_MODE=None
HOST="0.0.0.0"
PORT=5000

application/utils/__init__.py,在加载蓝图的过程中,自动加载socket服务端的api,代码:

def init_blueprint(app):
"""自动注册蓝图"""
......
# 加载蓝图内部的socket接口
try:
import_module(blueprint_path+".socket")
except:
pass

2.客户端引入socket.io.js

因为我们是基于python-socketio模块提供的服务端,所以客户端必须基于socketIO.js才能与其进行通信,所以客户端引入socketio.js。

socket.io.js的官方文档: https://socket.io/docs/v3

socket.io.js的github: https://github.com/socketio/socket.io/releases

我们可以新建一个orchard.html作为将来种植园模块的主页面,并在这个页面中使用socketio和服务端的flask-socketIO进行通信。

代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript" src="../static/js/socket.io.js"></script>
</head>
<body> <script>
// 命名空间
namespace = '/mofang';
var socket = io.connect('ws://192.168.20.251:5000' + namespace, {transports: ['websocket']});
// socket.on('connect', function() {
// console.log("客户端连接socket服务端");
// });
</script>
</body>
</html>

3.服务端创建orchard蓝图

服务端创建并注册蓝图目录orchard,终端命令如下:

cd application/apps/
python ../../manage.py blue -n=orchard

application/urls.py,代码:

from application.utils import include
urlpatterns = [
include("","home.urls"),
include("/users","users.urls"),
include("/marsh","marsh.urls"),
include("/orchard","orchard.urls"),

applicaion/settings/dev.py,代码:

    # 注册蓝图
INSTALLED_APPS = [
"application.apps.home",
"application.apps.users",
"application.apps.marsh",
"application.apps.orchard",
]

4.创建socket连接

在蓝图下面创建socket.py文件,并提供连接接口, orchard/socket.py

from application import socketio
from flask import request
@socketio.on("connect", namespace="/mofang")
def user_connect():
# request.sid socketIO基于客户端生成的唯一会话ID
print("用户%s连接过来了!" % request.sid) @socketio.on("disconnect", namespace="/mofang")
def user_disconnect():
print("用户%s退出了种植园" % request.sid )

5.客户端vue结合socketio

1.orchard.html

orchard.html客户端代码:

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt=""> </div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang_orchard',
token:"",
socket: null,
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.checkout(); },
methods:{
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
});
},
go_index(){
this.game.outWin("orchard");
},
}
});
}
</script>
</body>
</html>

2.orchard的CSS样式

css样式,main.css,代码:

.app .orchard-bg{
margin: 0 auto;
width: 100%;
max-width: 100rem;
position: absolute;;
z-index: -1;
top: -6rem;
}
.app .orchard-bg .board_bg2{
position: absolute;
top: 1rem;
}
.orchard .back{
position: absolute;
width: 3.83rem;
height: 3.89rem;
z-index: 1;
top: 2rem;
left: 2rem;
}
.orchard .music{
right: 2rem;
}
.orchard .header{
position: absolute;
top: 0rem;
left: 0;
right: 0;
margin: auto;
width: 32rem;
height: 19.28rem;
} .orchard .info{
position: absolute;
z-index: 1;
top: 0rem;
left: 4.4rem;
width: 8rem;
height: 9.17rem;
}
.orchard .info .avata{
width: 8rem;
height: 8rem;
position: relative;
}
.orchard .info .avatar_bf{
position: absolute;
z-index: 1;
margin: auto;
width: 6rem;
height: 6rem;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.orchard .info .user_avatar{
position: absolute;
z-index: 1;
width: 6rem;
height: 6rem;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
border-radius: 1rem;
}
.orchard .info .avatar_border{
position: absolute;
z-index: 1;
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 7.2rem;
height: 7.2rem;
}
.orchard .info .user_name{
position: absolute;
left: 8rem;
top: 1rem;
width: 11rem;
height: 3rem;
line-height: 3rem;
font-size: 1.5rem;
text-shadow: 1px 1px 1px #aaa;
border-radius: 3rem;
background: #ff9900;
text-align: center;
} .orchard .wallet{
position: absolute;
top: 3.4rem;
right: 4rem;
width: 16rem;
height: 10rem;
}
.orchard .wallet .balance{
margin-top: 1.4rem;
float: left;
margin-right: 1rem;
}
.orchard .wallet .title{
color: #fff;
font-size: 1.2rem;
width: 6.4rem;
text-align: center;
}
.orchard .wallet .title img{
width: 1.4rem;
margin-right: 0.2rem;
vertical-align: sub;
height: 1.4rem;
}
.orchard .wallet .num{
background: url("../images/btn3.png") no-repeat 0 0;
background-size: 100%;
width: 6.4rem;
font-size: 0.8rem;
color: #fff;
height: 2rem;
line-height: 1.8rem;
text-indent: 1rem;
}
.orchard .header .menu-list{
position: absolute;
top: 9rem;
left: 2rem;
}
.orchard .header .menu-list .menu{
color: #fff;
font-size: 1rem;
float: left;
width: 4rem;
height: 4rem;
text-align: center;
margin-right: 2rem;
}
.orchard .header .menu-list .menu img{
width: 3.33rem;
height: 3.61rem;
display: block;
margin: auto;
margin-bottom: 0.4rem;
}
.orchard .footer{
position: absolute;
width: 100%;
height: 6rem;
bottom: -2rem;
background: url("../images/board_bg3.png") no-repeat -1rem 0;
background-size: 110%;
}
.orchard .footer .menu-list{
width: 100%;
height: 4rem;
display: flex;
position: absolute;
top: -1rem;
}
.orchard .footer .menu-list .menu,
.orchard .footer .menu-list .menu-center{
float: left;
width: 4.44rem;
height: 5.2rem;
font-size: 1.5rem;
color: #fff;
line-height: 4.44rem;
text-align: center;
background: url("../images/btn5.png") no-repeat 0 0;
background-size: 100%;
flex: 1;
margin-left: 4px;
margin-right: 4px;
}
.orchard .footer .menu-list .menu-center{
background: url("../images/btn6.png") no-repeat 0 0;
background-size: 100%;
flex: 2;
}

orchard的CSS样式

6.基于事件接受信息

1.基于未定义事件进行通信

orchard.html客户端代码:

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
token:"",
socket: null,
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.checkout();
},
methods:{
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
this.login();
});
},
login(){
var id = this.game.fget("id");
// ***通过send方法可以直接发送数据,不需要自定义事件,数据格式是json格式***
this.socket.send({"uid":id});
},
go_index(){
this.game.outWin("orchard");
},
}
});
}
</script>
</body>
</html>

基于未定义事件进行通信

服务端orchard/socket.py代码:

from application import socketio
from flask import request # 未定义事件通信,客户端没有指定事件名称
@socketio.on("message",namespace="/mofang")
def user_message(data):
print("接收到来自%s发送的数据:" % request.sid)
print(">>>>data:",data)
print(">>>>uid:"data["uid"])

2.基于自定义事件进行通信

orchard.html客户端代码:

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
token:"",
socket: null,
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.checkout();
},
methods:{
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
this.login();
});
},
login(){
var id = this.game.fget("id");
// **自定义事件用emit提交**
this.socket.emit("login",{"uid":id});
},
go_index(){
this.game.outWin("orchard");
},
}
});
}
</script>
</body>
</html>

基于自定义事件进行通信

服务端orchard/socket.html代码:

from application import socketio
from flask import request # 自定义事件通信
@socketio.on("login", namespace="/mofang")
def user_login(data):
print("接受来自客户端%s发送的数据:" % request.sid)
print(data)
print(data["uid"])

3.服务端响应信息

1.服务端响应信息给客户端

服务端通过socketio.emit将内容响应给客户端

from application import socketio
from flask import request
from application.apps.users.models import User
# 建立socket通信
@socketio.on("connect", namespace="/mofang")
def user_connect():
# request.sid socketIO基于客户端生成的唯一会话ID
print("用户%s连接过来了!" % request.sid) # ***主动响应数据给客户端***
length = User.query.count()
socketio.emit("server_response",{"count":length},namespace="/mofang")

客户端接收响应信息,orchard.html代码:

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
token:"",
socket: null,
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.checkout();
},
methods:{
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
this.login();
this.get_count();
});
},
// **客户端接受响应信息**
get_count(){
this.socket.on("server_response",(res)=>{
this.game.print(res.count);
alert(`欢迎来到种植园,当前有${res.count}人在忙碌着~`)
});
},
login(){
var id = this.game.fget("id");
this.socket.emit("login",{"uid":id});
},
go_index(){
this.game.outWin("orchard");
},
}
});
}
</script>
</body>
</html>

客户端接受响应信息

2.基于房间管理分发信息

from application import socketio
from flask import request
from application.apps.users.models import User
from flask_socketio import join_room, leave_room # **** # 自定义事件通信
@socketio.on("login", namespace="/mofang")
def user_login(data):
print("接受来自客户端%s发送的数据:" % request.sid)
print(data) # ***一般基于用户id分配不同的房间***
room = data["uid"]
join_room(room)
socketio.emit("login_response", {"data": "登录成功"}, namespace="/mofang", room=room)

客户端代码:

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
token:"",
socket: null,
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.checkout();
},
methods:{
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
this.login();
this.get_count();
this.login_response(); //****
});
},
// ****
login_response(){
this.socket.on("login_response",(res)=>{
alert(res.data);
});
},
get_count(){
this.socket.on("server_response",(res)=>{
this.game.print(res.count);
alert(`欢迎${res.sid}来到种植园,当前有${res.count}人在忙碌着~`);
});
},
login(){
var id = this.game.fget("id");
this.socket.emit("login",{"uid":id});
},
go_index(){
this.game.outWin("orchard");
},
}
});
}
</script>
</body>
</html>

基于房间管理分发信息

3.服务端定时推送数据

from application import socketio
from flask import request
from application.apps.users.models import User
from flask_socketio import join_room, leave_room
# 建立socket通信
@socketio.on("connect", namespace="/mofang")
def user_connect():
# request.sid socketIO基于客户端生成的唯一会话ID
print("用户%s连接过来了!" % request.sid)
# 主动响应数据给客户端
length = User.query.count()
socketio.emit("server_response",{"count":length,"sid":"%s"% request.sid},namespace="/mofang") # 断开socket通信
@socketio.on("disconnect", namespace="/mofang")
def user_disconnect():
print("用户%s退出了种植园" % request.sid ) # 未定义事件通信,客户端没有指定事件名称
@socketio.on("message",namespace="/mofang")
def user_message(data):
print("接收到来自%s发送的数据:" % request.sid)
print(data)
print(data["uid"]) # 自定义事件通信
@socketio.on("login", namespace="/mofang")
def user_login(data):
print("接受来自客户端%s发送的数据:" % request.sid)
print(data)
# 一般基于用户id分配不同的房间
room = data["uid"]
join_room(room)
socketio.emit("login_response", {"data": "登录成功"}, namespace="/mofang", room=room) """***定时推送数据***"""
from threading import Lock
import random
thread = None
thread_lock = Lock() @socketio.on('chat', namespace='/mofang')
def chat(data):
global thread
with thread_lock:
if thread is None:
thread = socketio.start_background_task(target=background_thread) def background_thread(uid):
while True:
socketio.sleep(1)
t = random.randint(1, 100)
socketio.emit('server_response',
{'count': t},namespace='/mofang')

客户端代码:

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
<h1 style="position:absolute;top:20rem;">{{num}}</h1>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
token:"",
num:"",
socket: null,
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.checkout();
},
methods:{
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.settings.socket_namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
this.login();
this.get_count();
this.login_response();
});
},
login_response(){
this.socket.on("login_response",(res)=>{
alert(res.data);
});
},
get_count(){
this.socket.on("server_response",(res)=>{
this.num = res.count;
// alert(`欢迎${res.sid}来到种植园,当前有${res.count}人在忙碌着~`);
});
},
login(){
var id = this.game.fget("id");
// this.socket.emit("login",{"uid":id});
this.socket.emit("chat",{"uid":id}) // ****
},
go_index(){
this.game.outWin("orchard");
},
}
});
}
</script>
</body>
</html>

服务端定时推送数据

4.服务端推送广播信息

from flask_socketio import emit
@socketio.on('my_broadcast', namespace='/mofang')
def my_broadcast(data):
emit('broadcast_response', data, broadcast=True)
socketio.emit('some event', {'data': 42})
# 只要不声明房间ID,则默认返回给整个命名空间下所有的用户都可以接收

4.种植园页面展示

1.种植园主页面显示

主框架,html/orchard.html,代码:

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
<div class="header">
<div class="info">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<p class="user_name">好听的昵称</p>
</div>
<div class="wallet">
<div class="balance">
<p class="title"><img src="../static/images/money.png" alt="">钱包</p>
<p class="num">99,999.00</p>
</div>
<div class="balance">
<p class="title"><img src="../static/images/integral.png" alt="">果子</p>
<p class="num">99,999.00</p>
</div>
</div>
<div class="menu-list">
<div class="menu">
<img src="../static/images/menu1.png" alt="">
排行榜
</div>
<div class="menu">
<img src="../static/images/menu2.png" alt="">
签到有礼
</div>
<div class="menu">
<img src="../static/images/menu3.png" alt="">
道具商城
</div>
<div class="menu">
<img src="../static/images/menu4.png" alt="">
邮件中心
</div>
</div>
</div>
<div class="footer">
<ul class="menu-list">
<li class="menu">新手</li>
<li class="menu">背包</li>
<li class="menu-center">商店</li>
<li class="menu">消息</li>
<li class="menu">好友</li>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang_orchard',
token:"",
socket: null,
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.checkout();
},
methods:{
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
});
},
go_index(){
this.game.outWin("orchard");
},
}
});
}
</script>
</body>
</html>

种植园主页面显示:orchard.html

2.我的种植园页面显示

我的果园,html/my_orchard.html,代码:

<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard orchard-frame" id="app">
<div class="background">
<img class="grassland2" src="../static/images/grassland2.png" alt="">
<img class="mushroom1" src="../static/images/mushroom1.png" alt="">
<img class="stake1" src="../static/images/stake1.png" alt="">
<img class="stake2" src="../static/images/stake2.png" alt="">
</div>
<div class="pet-box">
<div class="pet">
<img class="pet-item" src="../static/images/pet1.png" alt="">
</div>
<div class="pet turned_off">
<img class="turned_image" src="../static/images/turned_off.png" alt="">
<p>请购买宠物</p>
</div>
</div>
<div class="tree-list">
<div class="tree-box">
<div class="tree">
<img src="../static/images/tree4.png" alt="">
</div>
<div class="tree">
<img src="../static/images/tree3.png" alt="">
</div>
<div class="tree">
<img src="../static/images/tree4.png" alt="">
</div>
</div>
<div class="tree-box">
<div class="tree">
<img src="../static/images/tree3.png" alt="">
</div>
<div class="tree">
<img src="../static/images/tree2.png" alt="">
</div>
<div class="tree">
<img src="../static/images/tree2.png" alt="">
</div>
</div>
<div class="tree-box">
<div class="tree">
<img src="../static/images/tree1.png" alt="">
</div>
<div class="tree">
<img src="../static/images/tree0.png" alt="">
</div>
<div class="tree">
<img src="../static/images/tree0.png" alt="">
</div>
</div>
</div>
<div class="prop-list">
<div class="prop">
<img src="../static/images/prop1.png" alt="">
<span>1</span>
<p>化肥</p>
</div>
<div class="prop">
<img src="../static/images/prop2.png" alt="">
<span>0</span>
<p>修剪</p>
</div>
<div class="prop">
<img src="../static/images/prop3.png" alt="">
<span>1</span>
<p>浇水</p>
</div>
<div class="prop">
<img src="../static/images/prop4.png" alt="">
<span>1</span>
<p>宠物粮</p>
</div>
</div>
<div class="pet-hp-list">
<div class="pet-hp">
<p>宠物1 饱食度</p>
<div class="hp">
<div style="width: 85%;" class="process">85%</div>
</div>
</div>
<div class="pet-hp">
<p>宠物2 饱食度</p>
<div class="hp">
<div style="width: 0;" class="process">0%</div>
</div>
</div>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
namespace: '/mofang_orchard',
token:"",
socket: null,
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){ },
methods:{ }
});
}
</script>
</body>
</html>

我的种植园页面显示:my_orchard.html

3.种植园CSS样式

css样式,代码:

.orchard-frame .background{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100rem;
}
.orchard-frame .background .grassland1{
width: 31.22rem;
height: 13.53rem;
position: absolute;
top: 4rem;
}
.orchard-frame .background .grassland2{
width: 31.22rem;
height: 13.53rem;
position: absolute;
top: 5rem;
}
.orchard-frame .background .mushroom1{
width: 4.56rem;
height: 4.83rem;
position: absolute;
right: 1rem;
top: 11rem;
}
.orchard-frame .background .stake1{
width: 4.56rem;
height: 4.83rem;
position: absolute;
top: 3rem;
left: 0rem;
}
.orchard-frame .background .stake2{
width: 6.31rem;
height: 4.83rem;
position: absolute;
top: 3rem;
left: 13rem;
}
.orchard-frame .pet-box{
position: absolute;
top: -2rem;
left: 0;
display: flex;
}
.orchard-frame .pet-box .pet{
position: relative;
width: 14.16rem;
height: 15rem;
flex: 1;
margin-left: 1rem;
margin-right: 1rem;
background: url("../images/tree1.png") no-repeat 0 -0.5rem;
background-size: 100%;
}
.orchard-frame .pet-box .turned_off .turned_image{
width: 5.14rem;
height: 6.83rem;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
.orchard-frame .pet-box .turned_off p{
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
border: 1px solid #fff;
border-radius: 1rem;
width: 8rem;
height: 3rem;
line-height: 3rem;
font-size: 1.5rem;
word-wrap: break-word;
padding: 1rem;
color: #000;
text-align: center;
background: rgba(255,255,255,.6);
}
.orchard-frame .pet-box .pet-item{
width: 10rem;
height: 10rem;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
.orchard-frame .tree-list{
position: absolute;
top: 9rem;
width: 100%;
}
.orchard-frame .tree-box{
margin-left: 3rem;
margin-right: 3rem;
}
.orchard-frame .tree-box .tree{
width: 9rem;
height: 4rem;
margin-bottom: 2rem;
float: left;
}
.orchard-frame .tree-box .tree img{
width: 9rem;
height: 8rem;
max-height: 8rem;
}
.orchard-frame .prop-list{
position: absolute;
bottom: 6rem;
width: 100%;
}
.orchard-frame .prop-list .prop{
float: left;
margin-left: 1rem;
width: 3rem;
position: relative;
}
.orchard-frame .prop-list .prop img{
width: 2.5rem;
height: 2.5rem;
margin: auto;
display: block;
}
.orchard-frame .prop-list .prop span{
position: absolute;
top: -4px;
right: -4px;
border-radius: 50%;
width: 1rem;
height; 1rem;
font-size: .8rem;
color: #fff;
background-color: #cc0000;
text-align: center;
line-height: 1rem;
padding: 2px;
}
.orchard-frame .prop-list .prop p{
text-align: center;
color: #fff;
}
.orchard-frame .pet-hp-list{
position: absolute;
right: 0;
bottom: 8rem;
width: 11rem;
height: 4rem;
}
.orchard-frame .pet-hp-list .pet-hp{
margin-bottom: 5px;
}
.orchard-frame .pet-hp-list .pet-hp p{ }
.orchard-frame .pet-hp-list .pet-hp .hp{
border: 1px solid #fff;
border-radius: 5rem;
width: 10rem;
padding: 1px;
}
.orchard-frame .pet-hp-list .pet-hp .process{
font-size: 0.5rem;
background-color: red;
color: #fff;
border-radius: 5rem;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
text-align: center;
}

种植园CSS样式代码

day112:MoFang:种植园使用websocket代替http&服务端基于flask-socketio提供服务&服务端响应信息&种植园页面显示初始化的更多相关文章

  1. [转]python实现RESTful服务(基于flask)

    python实现RESTful服务(基于flask) 原文: https://www.jianshu.com/p/6ac1cab17929  前言 上一篇文章讲到如何用java实现RESTful服务, ...

  2. .net core微服务之基于Docker+Consul+Registrator服务注册服务发现

    一.Docker部分: 先拉最新的asp.net core的镜像: docker pull microsoft/aspnetcore 将下载下来的镜像重命名,为什么要重命名?等会讲Registrato ...

  3. .net core 跨平台开发 微服务架构 基于Nginx反向代理 服务集群负载均衡

    1.概述 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客 ...

  4. python实现RESTful服务(基于flask)

    https://www.jianshu.com/p/6ac1cab17929 http://www.pythondoc.com/flask/quickstart.html 在java中调用python ...

  5. python实现Restful服务(基于flask)(2)

    参考:https://blog.csdn.net/yelena_11/article/details/53404892 最简单的post例子: from flask import Flask, req ...

  6. iUAP云运维平台v3.0全面支持基于K8s的微服务架构

    什么是微服务架构? 微服务(MicroServices)架构是当前互联网业界的一个技术热点,业内各公司也都纷纷开展微服务化体系建设.微服务架构的本质,是用一些功能比较明确.业务比较精练的服务去解决更大 ...

  7. 微服务架构:构建PHP微服务生态

    微服务架构:构建PHP微服务生态   Linux系统技术交流QQ群(1675603)验证问题答案:刘遄 导读 诞生于 2014 年的“微服务架构”,其思想经由 Martin Fowler 阐述后,在近 ...

  8. 【认知服务 Azure Cognitive Service】使用认知服务的密钥无法访问语音服务[ErrorCode=AuthenticationFailure] (2020-08时的遇见的问题,2020-09月已解决)

    问题情形 根据微软认知服务的文档介绍,创建认知服务(Cognitive Service)后,可以调用微软的影像(计算机视觉,人脸),语言(LUIS, 文本分析,文本翻译),语音(文本转语音,语音转文本 ...

  9. 通过Dapr实现一个简单的基于.net的微服务电商系统(十二)——istio+dapr构建多运行时服务网格

    多运行时是一个非常新的概念.在 2020 年,Bilgin Ibryam 提出了 Multi-Runtime(多运行时)的理念,对基于 Sidecar 模式的各种产品形态进行了实践总结和理论升华.那到 ...

随机推荐

  1. vue的html2canvas将dom转化为图片时踩得坑

    一.html2canvas中图片涉及跨域图片 应用场景:做个投票活动,将参赛者的信息转化成图片截图分享.用户上传图片上传到腾讯云cos桶中,html2canvas只能转换本地资源的图片,涉及跨域的图片 ...

  2. Docker这么火爆。章节一:带你详尽了解Docker容器的介绍及使用

    前言 很多小伙伴可能在工作中都听说过Docker,但是实际工作中却没有使用过,听得多了,也对Docker内心有一种很深切的想了解,但是因为各种原因而不知道如何去了解而发愁,不要急,这篇文章带你认识Do ...

  3. iMindMap不同视图的应用技巧介绍

    在刚开始使用iMindMap思维导图软件时,很多用户会习惯性地使用默认的Mind Map视图.因该视图布局自由,用户可以发挥自我创造力,进行多种形式的思维图表创建. 其实,除此之外,iMindMap还 ...

  4. Sound Forge批量转换音频格式,实现高效编辑音频

    Sound Forge的批量处理功能可以实现批量格式转换.批量添加效果等功能,让用户可以在处理其他音频编辑任务的同时,自动完成格式转换.效果添加等重复性任务.接下来,一起来看看如何借助批处理转换器实现 ...

  5. IDM下载器:站点抓取相关设置介绍

    Internet Download Manager(简称IDM)是一款十分好用资源下载器,它的站点抓取功能不仅可以下载被过滤器指定所需文件,例如一个站点的所有图片,或者一个站点的所有音频,也可以下载站 ...

  6. 安装Ubuntu时到底该如何分区

    安装系统:Ubuntu16.04(单系统) /(根分区),主分区,   Ext4文件系统,100G-200G /boot分区,   逻辑分区,Ext4文件系统,~200MB /home分区, 逻辑分区 ...

  7. Windows10通过NFS挂载linux目录

    大致分为以下三大步骤: 一.启动NFS服务器 二.启动NFS客户端 三.挂载NFS目录 工具: win10.虚拟机Ubuntu18.0系统 一. 启动linux的NFS服务端: 以下均为Ubuntu操 ...

  8. 【Luogu U41492】树上数颜色——树上启发式合并(dsu on tree)

    (这题在洛谷主站居然搜不到--还是在百度上偶然看到的) 题目描述 给一棵根为1的树,每次询问子树颜色种类数 输入输出格式 输入格式: 第一行一个整数n,表示树的结点数 接下来n-1行,每行一条边 接下 ...

  9. LaTeX中的数学公式之矩阵

    矩阵的代码及注释: 显示效果:

  10. PyQt(Python+Qt)学习随笔:QMdiArea多文档界面区域的viewMode、documentMode、tabsClosable、tabPosition等属性介绍

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 viewMode属性用于控制子窗口是使用子窗口模式(QMdiArea. ...