Python中的WebSocket
一、Websockets介绍
随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了。近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端的通信功能,使服务端也能主动向客户端发送数据。
我们知道,传统的HTTP协议是无状态的,每次请求(request)都要由客户端(如
浏览器)主动发起,服务端进行处理后返回response结果,而服务端很难主动向客户端发送数据;这种客户端是主动方,服务端是被动方的传统Web模式
对于信息变化不频繁的Web应用来说造成的麻烦较小,而对于涉及实时信息的Web应用却带来了很大的不便,如带有即时通信、实时数据、订阅推送等功能的应
用。在WebSocket规范提出之前,开发人员若要实现这些实时性较强的功能,经常会使用折衷的解决方法:轮询(polling)和Comet技术。其实后者本质上也是一种轮询,只不过有所改进。
轮询是最原始的实现实时Web应用的解决方案。轮询技术要求客户端以设定的时间间隔周期性地向服务端发送请求,频繁地查询是否有新的数据改动。明显地,这种方法会导致过多不必要的请求,浪费流量和服务器资源。
Comet技术又可以分为长轮询和流技术。长轮询改进了上述的轮询技术,减小了无用的请求。它会为某些数据设定过期时间,当数据过期后才会向服务端发送请求;这种机制适合数据的改动不是特别频繁的情况。流技术通常是指客户端使用一个隐藏的窗口与服务端建立一个HTTP长连接,服务端会不断更新连接状态以保持HTTP长连接存活;这样的话,服务端就可以通过这条长连接主动将数据发送给客户端;流技术在大并发环境下,可能会考验到服务端的性能。
这两种技术都是基于请求-应答模式,都不算是真正意义上的实时技术;它们的每一次请求、应答,都浪费了一定流量在相同的头部信息上,并且开发复杂度也较大。
伴随着HTML5推出的WebSocket,真正实现了Web的实时通信,使B/S模式具备了C/S模式的实时通信能力。WebSocket的工作流程是这
样的:浏览器通过JavaScript向服务端发出建立WebSocket连接的请求,在WebSocket连接建立成功后,客户端和服务端就可以通过
TCP连接传输数据。因为WebSocket连接本质上是TCP连接,不需要每次传输都带上重复的头部数据,所以它的数据传输量比轮询和Comet技术小了很多.
关于websocket的优点可以看:http://www.tuicool.com/articles/7zyMvy6
二,Python中的WebSocket
Django的WebSocket:
1.dwebsocket: https://www.cnblogs.com/huguodong/p/6611602.html
2.channels: http://www.cnblogs.com/evilliu/articles/6529087.html
参考链接:https://www.cnblogs.com/jingmoxukong/p/7755643.html
这里使用flask作为服务器,python版本为3.6.5
安装模块
- pip install gevent-websocket
群聊
- from flask import Flask,request,render_template
- from geventwebsocket.handler import WebSocketHandler
- from gevent.pywsgi import WSGIServer
- from geventwebsocket.websocket import WebSocket
- import json
- app = Flask(__name__)
- user_dict = {} # 空字典,用来存放用户名和发送消息
- @app.route("/<username>") # 参数为用户名
- def index(username):
- # 获取请求的WebSocket对象
- user_socket = request.environ.get("wsgi.websocket") # type:WebSocket
- if user_socket:
- # 设置键值对
- # {'xiao': <geventwebsocket.websocket.WebSocket object at 0x0000020F6F6B8DB0>}
- user_dict[username] = user_socket
- print(user_dict)
- # 循环,接收消息
- while True:
- # 接收消息
- msg = user_socket.receive()
- print(msg)
- # 反序列化数据,因为前端发送的是json
- recv_msg = json.loads(msg)
- print(recv_msg)
- # 构造数据结构
- send_msg = {
- # 获取用户名
- "username":recv_msg.get("username"),
- # 获取消息
- "msg":recv_msg.get("msg")
- }
- # 遍历字典
- for i in user_dict.values():
- # 这里的i就是websocket对象
- # 判断websocket对象等于请求的websocket对象
- if i == user_socket:
- # 跳过循环
- continue
- # 发送数据,对数据做序列化
- i.send(json.dumps(send_msg))
- @app.route("/ws")
- def ws():
- return render_template("many_people.html")
- if __name__ == '__main__':
- # 创建一个WebSocket服务器
- http_serv = WSGIServer(("0.0.0.0",),app,handler_class=WebSocketHandler)
- # 开始监听HTTP请求
- http_serv.serve_forever()
- # app.run("0.0.0.0", , debug=True)
前端
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- 你的昵称:<input type="text" id="nickname">
- <button onclick="connws()">连接服务器</button>
- <br><br>
- 发送消息:<input type="text" id="talk">
- <button onclick="send_msg()">发送信息</button><br><br>
- <div style="width: 500px;height: 100%;border: 1px red solid;" id="text">
- </div>
- </body>
- <script type="application/javascript">
- var user_name = null; //用户名
- var ws = null; //WebSocket 对象,默认设置为空
- //连接ws
- function connws() {
- //获取输入框中的用户名
- user_name = document.getElementById("nickname").value;
- //创建 WebSocket 对象
- ws = new WebSocket("ws://127.0.0.1:5000/" + user_name);
- //客户端接收服务端数据时触发
- ws.onmessage = function (data) {
- // 反序列化接收数据
- var recv_msg = JSON.parse(data.data);
- console.log(recv_msg);
- // 执行自定义函数createDiv,传入2个参数
- createDiv(recv_msg.username, recv_msg.msg);
- };
- }
- //发送消息
- function send_msg() {
- // 获取输入框的发送消息
- var talk = document.getElementById("talk").value;
- // 执行自定义函数createDiv
- createDiv("w", talk);
- // 组件发送数据对象
- send_str = {
- username:user_name, //用户名
- msg:talk //消息
- };
- //使用连接发送数据,序列化对象
- ws.send(JSON.stringify(send_str));
- };
- //显示聊天信息
- function createDiv(self, content) {
- // 创建div标签
- var divtag = document.createElement("div");
- //定义格式
- var who = self + " : ";
- // 判断参数为w时
- if (self == "w") {
- // 替换字符串
- who = "我 : "
- }
- // 修改显示框的text属性
- divtag.innerText = who + content;
- // 获取显示框
- var text = document.getElementById("text");
- // appendChild() 方法向节点添加最后一个子节点
- // 添加一个div标签
- text.appendChild(divtag);
- }
- </script>
- </html>
单聊
- from flask import Flask,request,render_template
- from geventwebsocket.handler import WebSocketHandler
- from gevent.pywsgi import WSGIServer
- from geventwebsocket.websocket import WebSocket
- import json
- app = Flask(__name__)
- user_dict = {} # 空字典,用来存放用户名和发送消息
- @app.route("/<username>") # 参数为用户名
- def index(username):
- # 获取请求的WebSocket对象
- user_socket = request.environ.get("wsgi.websocket") # type:WebSocket
- if user_socket:
- # 设置键值对
- # {'xiao': <geventwebsocket.websocket.WebSocket object at 0x0000020F6F6B8DB0>}
- user_dict[username] = user_socket
- print(user_dict)
- # 循环,接收消息
- while True:
- # 接收消息
- msg = user_socket.receive()
- # print(msg)
- # 反序列化数据,因为前端发送的是json
- recv_msg = json.loads(msg)
- print(recv_msg)
- # 构造数据结构
- send_msg = {
- # 消息
- "msg": recv_msg.get("msg"),
- # 来自于哪个用户
- "from_user": username,
- }
- # 获取聊天对象的名字
- to_user = user_dict.get(recv_msg.get("to_user"))
- # 发送数据
- to_user.send(json.dumps(send_msg))
- @app.route("/ws")
- def ws():
- return render_template("single_chat.html")
- if __name__ == '__main__':
- # 创建一个WebSocket服务器
- http_serv = WSGIServer(("0.0.0.0",5000),app,handler_class=WebSocketHandler)
- # 开始监听HTTP请求
- http_serv.serve_forever()
- # app.run("0.0.0.0", 5000, debug=True)
后端
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- 你的昵称:<input type="text" id="nickname">
- <button onclick="connws()">连接服务器</button>
- <br>
- 与谁说话:<input type="text" id="sender">
- <br>
- 发送消息:<input type="text" id="talk">
- <button onclick="send_msg()">发送信息</button><br/><br/>
- <div style="width: 500px;height: 100%;border: 1px red solid;" id="text">
- </div>
- </body>
- <script type="application/javascript">
- var user_name = null; //用户名
- var ws = null; //WebSocket 对象,默认设置为空
- //连接ws
- function connws() {
- //获取输入框中的用户名
- user_name = document.getElementById("nickname").value;
- //创建 WebSocket 对象
- ws = new WebSocket("ws://127.0.0.1:5000/" + user_name);
- //客户端接收服务端数据时触发
- ws.onmessage = function (data) {
- // 反序列化接收数据
- var recv_msg = JSON.parse(data.data);
- console.log(recv_msg);
- // 执行自定义函数createDiv,传入2个参数
- createDiv(recv_msg.from_user, recv_msg.msg);
- };
- }
- function send_msg() {
- // 获取输入框的发送消息
- var talk = document.getElementById("talk").value;
- // 获取输入框的聊天对象
- var sender = document.getElementById("sender").value;
- // 执行自定义函数createDiv
- createDiv("w", talk);
- // 构造发送数据对象
- send_str = {
- msg:talk, //消息
- to_user:sender, //对方
- };
- //使用连接发送数据,序列化对象
- ws.send(JSON.stringify(send_str));
- };
- //显示聊天信息
- function createDiv(self, content) {
- // 创建div标签
- var divtag = document.createElement("div");
- //定义格式
- var who = self + " : ";
- // 判断参数为w时
- if (self == "w") {
- // 替换字符串
- who = "我 : "
- }
- // 修改显示框的text属性
- divtag.innerText = who + content;
- // 获取显示框
- var text = document.getElementById("text");
- // appendChild() 方法向节点添加最后一个子节点
- // 添加一个div标签
- text.appendChild(divtag);
- }
- </script>
- </html>
前端
相关拓展:https://segmentfault.com/a/1190000010140660
Python中的WebSocket的更多相关文章
- python测试基于websocket协议的即时通讯接口
随着html5的广泛应用,基于websocket协议的即时通讯有了越来越多的使用场景,本文使用python中的websocket-client模块来做相关的接口测试 import webclient ...
- [转]Python中的str与unicode处理方法
早上被python的编码搞得抓耳挠腮,在搜资料的时候感觉这篇博文很不错,所以收藏在此. python2.x中处理中文,是一件头疼的事情.网上写这方面的文章,测次不齐,而且都会有点错误,所以在这里打算自 ...
- python中的Ellipsis
...在python中居然是个常量 print(...) # Ellipsis 看别人怎么装逼 https://www.keakon.net/2014/12/05/Python%E8%A3%85%E9 ...
- python中的默认参数
https://eastlakeside.gitbooks.io/interpy-zh/content/Mutation/ 看下面的代码 def add_to(num, target=[]): tar ...
- Python中的类、对象、继承
类 Python中,类的命名使用帕斯卡命名方式,即首字母大写. Python中定义类的方式如下: class 类名([父类名[,父类名[,...]]]): pass 省略父类名表示该类直接继承自obj ...
- python中的TypeError错误解决办法
新手在学习python时候,会遇到很多的坑,下面来具体说说其中一个. 在使用python编写面向对象的程序时,新手可能遇到TypeError: this constructor takes no ar ...
- python中的迭代、生成器等等
本人对编程语言实在是一窍不通啊...今天看了廖雪峰老师的关于迭代,迭代器,生成器,递归等等,word天,这都什么跟什么啊... 1.关于迭代 如果给定一个list或tuple,我们可以通过for循环来 ...
- python2.7高级编程 笔记二(Python中的描述符)
Python中包含了许多内建的语言特性,它们使得代码简洁且易于理解.这些特性包括列表/集合/字典推导式,属性(property).以及装饰器(decorator).对于大部分特性来说,这些" ...
- python cookbook 学习系列(一) python中的装饰器
简介 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象.它经常用于有切面需求的场景,比如:插入日志.性能测试.事务处理.缓 ...
随机推荐
- Extjs5 app.js缓冲设置
在6月2日Extjs5正式版公布后.粗略研究了一下,sencha推荐使用project编译来公布应用.开发过程中用sencha app watch命令就可以生成服务.每建立一个js类,就须要Ctrl+ ...
- 【ARDUINO】蓝牙(HC-05)透传
1.蓝牙连接ARDUINO 工作模式:VCC-5.5V GND-GND TXD-RX RXD-TX 工作模式下默认波特率38400 AT模式,在工作模式的基础上KEY-VCC/5.5V 设置从模式: ...
- VS2010程序打包操作(结合图片详细讲解)
附视频教程:http://www.cnblogs.com/mengdesen/archive/2011/06/14/2080312.html 1. 在vs2010 选择“新建项目”----“其他项 ...
- script跨域之360搜索
思考: 布局: 1,flex元素上下左右居中,内部元素横向排列: div{ /* 100vh = viewport height*/ display: flex; justify-content: c ...
- 【BZOJ4711】小奇挖矿 树形DP
[BZOJ4711]小奇挖矿 Description [题目背景] 小奇在喵星系使用了无限非概率驱动的采矿机,以至于在所有星球上都采出了一些矿石,现在它准备建一些矿石仓库并把矿石运到各个仓库里. [问 ...
- 【BZOJ4260】Codechef REBXOR Trie树+贪心
[BZOJ4260]Codechef REBXOR Description Input 输入数据的第一行包含一个整数N,表示数组中的元素个数. 第二行包含N个整数A1,A2,…,AN. Output ...
- 爬虫实战【12】使用cookie登陆豆瓣电影以及获取单个电影的所有短评
昨天我们已经实现了如何抓取豆瓣上的热门电影信息,虽然不多,只有几百,但是足够我们进行分析了. 今天我们来讲一下如何获取某一部电影的所有短评论信息,并保存到mongodb中. 反爬虫 豆瓣设置的反爬虫机 ...
- 【Git和GitHub】学习笔记
1. 书籍推荐: 先看一本比较简单并且好的入门书籍 Git - Book https://git-scm.com/book/zh/v2 2. 书籍理解: Git 有三种状态,你的文件可能处于其中之一: ...
- XML 解析之 dom4j 解析器
dom4j 的使用需要导入 jar 包, 包括: dom4j-1.6.1 和 jaxen-1.1-beta 步骤: 在项目目录下,"Folder" 创建一个 lib 文件夹 复制 ...
- 细数Python中的数据类型以及他们的方法
一.数据类型的种类及主要功能 1.数字类型 数字类型主要是用来计算,它分为整数类型int和浮点类型float 2.布尔类型 布尔类型主要是用于判断,它分为真True和False两种 3.字符串类型 字 ...