0. 前言
最近找同学参加了嵌入式比赛,为了让自己的简历不显得一穷二白,可惜我本人是计算机专业的学生,因此大部分的工作是上位机开发,可能也会搞一下WIFI模块。 由于在此过程中还是学到了一些东西,因此我打算以博客的形式记录之,以便后续浏览,也希望大家可以从我的博客中有所收获。 本人争取按时更新。
1. 本次任务介绍
目前接到的需求是上位机使用Web界面(最早的设想是GUI),将下位机传来的数据接收(使用TCP传输,后续可能改为UDP),并做处理后放到web网页上。因此我需要的工作是: 1.TCP server的建立(上位机做服务器,下位机做客户端) 2. Web界面的实现 3. TCP server向web server发送数据。
考虑到本人贫瘠的编程能力和web开发的需求,因此我打算使用Python开发。
TCP server使用socket库实现,web界面使用flask实现,通信则用requests由TCP server向web server的指定URL发送POST请求实现。
以下我将会介绍具体内容
2. TCP server的建立
Python中,可以使用socket库构建TCP server。 本人使用的是socketserver模块,其实也是基于socket的,使用这个模块的好处是可以更加方便的建立TCP server,甚至省去了创建时的绑定IP端口,调用accept阻塞的方法。
调用时的方法如下:
# 建立TCP server
def start_TCP(host,port):
myserver=socketserver.ThreadingTCPServer((host,port),MyHandler)
print("You have start server,address is: {}:{}".format(host,port))
#持续服务
myserver.serve_forever()
我们注意到,在构造类ThreadingTCPServer时的第二个参数是MyHandler,这个是自定义的类,继承了socketserver.BaseRequestHandler类,需要重写handle函数,具体如下:
class MyHandler(socketserver.BaseRequestHandler):
def handle(self):
while True:
#接收数据,处理
data=self.request.recv(1024)
try:
data=data.decode("gbk")
except:
#主要是防止编码错误问题
data="cannot show normally!"
#稍作包装
object={"data":time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())+" "+data}
#没有接收到数据,不向web server转发
if not data:
break
else:
#本来打算做成线程间通信的,后来一想是web server,直接发post请求好了
print("Got: {}".format(data))
self.request.send("You have successfully sent data!".encode("ascii"))
with requests.post("http://192.168.71.1:5000/",json=object) as r:
pass
具体的处理过程是:
调用self.requests.recv,接收数据,参数为接收长度对数据进行处理,可以调用self.request.send向客户端回送消息
至于向web server转发的内容,后文会介绍。
3. Flask web的建立
flask是一个开源web框架,学起来非常简单,只要有一定的web基础(甚至是常识)就能学会。 具体的代码如下:
from flask import *
import threading
app=Flask(__name__)
#一些全局变量
messages=[]
length=0
#主页面
@app.route("/",methods=["GET","POST"])
def index_page():
global messages
#接收TCP server发送的数据
data=request.get_json()
#若有数据,则相应操作,这里是加入到全局列表中
if data is not None:
messages.append(data["data"])
#渲染模板
return render_template("index0.html",messages=messages)
#ajax会向此路由发送post请求轮询,用于异步更新路由
@app.route("/get_data",methods=['GET','POST'])
def get_data():
#发现有新数据加入的时候,要求前端进行相应动作,例如更新路由
global length
if(length length=len(messages) return "flash" return "" 其中前端代码如下 首先是index0.html,因为是学习部分,就放了几个数据
历史信息
{% for each in messages %}
{{each}}
{% endfor %}
这是比较简单的flask模板的编写,这里不再进行叙述。 然后是用到的JavaScript代码。很抱歉,本人对js并不熟悉,这些代码也是从网上找+自己改的,因此不能给出具体的解释。
var script=document.createElement("script");
script.type="text/javascript";
script.src="jquery.js";
//ajax轮询,每100ms调用一次函数,即ajax请求
setInterval(
function(){
$.ajax({
url:"/get_data",
async:true,
type:'post', //貌似post才行,我很疑惑
timeout:10000,
success:function(data){
if(data=="flash"){
//your operation
window.location.reload();//刷新路由,也可以是其他操作
}
},
error:function(xhr,type){
window.alert("error!");
}
})
},100
)
4. TCP server 向web server通信
这里就用到了一行,即requests.post请求 并且前面也已经展示了
# 本实验中,web server的ip和端口为192.168.71.1:5000
with requests.post("http://192.168.71.1:5000/",json=object) as r:
pass
5. 总程序
总程序用来启动web server和TCP server,由于两个server都是持久服务,因此在主进程中启动一个就会陷入死循环,不能启动另一个,因此我将这两个服务器启动放到了两个线程上,并且设置了不同的端口号防止冲突。 (其实这里我一直陷入困惑,因为按照我浅显的计网知识,我觉得两个server应该在两个进程上,可放到两个线程上貌似也可以,反而是多进程失败了……后续有时间会探求一下)
### index.py
from flask import *
import threading
import socketserver
import time
# web服务器部分
from app_views import *
#TCP服务器部分
from lib.MyServer import *
def start_TCP(host,port):
myserver=socketserver.ThreadingTCPServer((host,port),MyHandler)
print("You have start server,address is: {}:{}".format(host,port))
myserver.serve_forever()
def start_flask(host,port):
print("you have start flask server,address is: {}:{}".format(host,port))
app.run(host=host,port=port)
def main():
th1=threading.Thread(target=start_flask,args=("192.168.71.1",5000))
th2=threading.Thread(target=start_TCP,args=("192.168.71.1",5001))
th1.start()
th2.start()
if __name__=="__main__":
main()
6.验收
启动上述代码,打开网络调试助手,向192,168.71.1:5001发送数据,可以看到在前端已正常显示,证明实验成功。
7. 可改进的地方
可不可以直接向web server发送数据?我不好说,因为感觉还是要建立TCP连接才行有没有其他转交方式?可以线程间共享变量,使用信号量控制发送接收关系,但可能很麻烦前端更新数据除了ajax轮询还有没有更好的方式?可不可以实现真正的异步通讯,以免不必要的负载?
本篇文章到此,感谢收看!
推荐链接
发表评论