flask 轮询/长轮询/长连接 websocket
flask 轮询 长轮询 长连接
轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。
优点:后端程序编写比较容易。
缺点:请求中有大半是无用,浪费带宽和服务器资源。(而每一次的 HTTP 请求和应答都带有完整的 HTTP 头信息,这就增加了每次传输的数据量)
实例:适于小型应用。
长轮询:客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接(或到了设定的超时时间关闭连接),客户端处理完响应信息后再向服务器发送新的请求。
优点:在无消息的情况下不会频繁的请求,节省了网络流量,解决了服务端一直疲于接受请求的窘境
缺点:服务器hold连接会消耗资源,需要同时维护多个线程,服务器所能承载的TCP连接数是有上限的,这种轮询很容易把连接数顶满。
实例:WebQQ、Hi网页版、Facebook IM。
长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。
连接保持 - Http 发起请求 在请求中写一个协议 - WebSocket - 服务器收到Websocket请求 ,自动保持此连接 - 永久不断开,除非主动断开 - 可以通过此连接主动找到客户端
优点:消息即时到达,不发无用请求。
缺点:服务器维护一个长连接会增加开销。
实例:Gmail聊天
WebSocket:
WebSocket 协议本质上是一个基于 TCP 的协议。为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息”Upgrade: WebSocket”表 明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。
websocket
安装: pip3 install gevent-websocket
http headers environ
{'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_SOFTWARE': 'gevent/1.4 Python/3.6', 'SCRIPT_NAME': '', 'wsgi.version': (1, 0), 'wsgi.multithread': False, 'wsgi.multiprocess': False, 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'SERVER_NAME': 'LAPTOP-LPOO7NRF', 'SERVER_PORT': '9988', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/my_socket', 'QUERY_STRING': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REMOTE_ADDR': '192.168.16.60', 'REMOTE_PORT': '57375', 'HTTP_HOST': '192.168.16.60:9988', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9,en;q=0.8', 'wsgi.input': <gevent.pywsgi.Input object at 0x000001B65E80DF48>, 'wsgi.input_terminated': True, 'werkzeug.request': <Request 'http://192.168.16.60:9988/my_socket' [GET]>} Host: 192.168.16.60:9988 Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
ws headers environ
{'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_SOFTWARE': 'gevent/1.4 Python/3.6', 'SCRIPT_NAME': '', 'wsgi.version': (1, 0), 'wsgi.multithread': False, 'wsgi.multiprocess': False, 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'SERVER_NAME': 'LAPTOP-LPOO7NRF', 'SERVER_PORT': '9988', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/my_socket', 'QUERY_STRING': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'REMOTE_ADDR': '127.0.0.1', 'REMOTE_PORT': '57569', 'HTTP_HOST': '127.0.0.1:9988', 'HTTP_CONNECTION': 'Upgrade', 'HTTP_PRAGMA': 'no-cache', 'HTTP_CACHE_CONTROL': 'no-cache', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36', 'HTTP_UPGRADE': 'websocket', 'HTTP_ORIGIN': 'http://localhost:63342', 'HTTP_SEC_WEBSOCKET_VERSION': '13', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9,en;q=0.8', 'HTTP_COOKIE': 'csrftoken=Ny8QCs5MxKUWf5HnN8Yr51cCfdj42bFOZJaq5pc8xwSi97ZvwgOp8QYwET1RH1KB; sessionid=t1v6cv4es95y102k5ruzqemn4i28bfdz', 'HTTP_SEC_WEBSOCKET_KEY': 'TQd57FoFfA/ejI3oDQR+zg==', 'HTTP_SEC_WEBSOCKET_EXTENSIONS': 'permessage-deflate; client_max_window_bits', 'wsgi.input': <gevent.pywsgi.Input object at 0x000001B65E80DEE8>, 'wsgi.input_terminated': True, 'wsgi.websocket_version': '13', 'wsgi.websocket': <geventwebsocket.websocket.WebSocket object at 0x000001B65E81F868>, 'werkzeug.request': <Request 'http://127.0.0.1:9988/my_socket' [GET]>} Host: 127.0.0.1:9988 Connection: Upgrade Pragma: no-cache Cache-Control: no-cache User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36 Upgrade: websocket Origin: http://localhost:63342 Sec-Websocket-Version: 13 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Cookie: csrftoken=Ny8QCs5MxKUWf5HnN8Yr51cCfdj42bFOZJaq5pc8xwSi97ZvwgOp8QYwET1RH1KB; sessionid=t1v6cv4es95y102k5ruzqemn4i28bfdz Sec-Websocket-Key: TQd57FoFfA/ejI3oDQR+zg== Sec-Websocket-Extensions: permessage-deflate; client_max_window_bits
websocket 基本样式
# http flask 浏览器 # Websocket GeventWebsocket +Flask 客户端 JavaScript(Websocket客户端) from flask import Flask, request, render_template # 提供WS协议服务 from geventwebsocket.handler import WebSocketHandler # 承载服务 from geventwebsocket.server import WSGIServer # 语法提示 from geventwebsocket.websocket import WebSocket from flask import Flask app = Flask(__name__) @app.route("/my_socket") def my_socket(): # 获取当前客户端和服务端的Socket 的连接 后面的type 是指定类型 提示信息 # print(request.environ.get("wsgi.websocket")) #type :WebSocket # print(request.headers) user_socket = request.environ.get("wsgi.websocket") # type :WebSocket print(user_socket, "连接已经建立了") while True: msg = user_socket.receive() print(msg) user_socket.send(msg) print(user_socket) return "200 okkk" if __name__ == '__main__': # app.run("0.0.0.0", 9988) http_serve = WSGIServer(("0.0.0.0", 9988), app, handler_class=WebSocketHandler) http_serve.serve_forever()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> </body> <script type="application/javascript"> var ws = new WebSocket("ws://127.0.0.1:9988/my_socket"); ws.onmessage = function (MessageEvent) { console.log(MessageEvent.data); }; </script> </html>
无名群聊
用列表形式 存储所有链接 异常处理 断掉的链接
# http flask 浏览器 # Websocket GeventWebsocket +Flask 客户端 JavaScript(Websocket客户端) from flask import Flask, request, render_template # 提供WS协议服务 from geventwebsocket.handler import WebSocketHandler # 承载服务 from geventwebsocket.server import WSGIServer # 语法提示 from geventwebsocket.websocket import WebSocket from flask import Flask app = Flask(__name__) user_socket_list = [] @app.route("/my_socket") def my_socket(): # 获取当前客户端和服务端的Socket 的连接 后面的type 是指定类型 提示信息 user_socket = request.environ.get("wsgi.websocket") # type : WebSocket if user_socket: user_socket_list.append(user_socket) print(len(user_socket_list), user_socket_list) while True: msg = user_socket.receive() print(msg) for usocket in user_socket_list: # 异常处理 比如某一个断开了连接 try: usocket.send(msg) except: continue # return "200 okkk" @app.route("/gc") def gc(): return render_template("gc.html") if __name__ == '__main__': http_serve = WSGIServer(("0.0.0.0", 9988), app, handler_class=WebSocketHandler) http_serve.serve_forever()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>无名群聊</title> </head> <body> <p><input type="text" id="content"> <button onclick="send_msg()"> 发送消息</button></p> <div id="chat_list"></div> </body> <script type="application/javascript"> var ws = new WebSocket("ws://192.168.16.60:9988/my_socket"); // 监听 ws.onmessage = function (MessageEvent) { console.log(MessageEvent.data); var p=document.createElement("p"); p.innerText=MessageEvent.data; document.getElementById("chat_list").appendChild(p); }; function send_msg(){ var content=document.getElementById("content").value; ws.send(content); } </script> </html>
有名群聊
字典形式存储
from flask import Flask, request, render_template # 提供WS协议服务 from geventwebsocket.handler import WebSocketHandler # 承载服务 from geventwebsocket.server import WSGIServer # 语法提示 from geventwebsocket.websocket import WebSocket from flask import Flask app = Flask(__name__) user_socket_dict = {} # 注意添加用户参数 @app.route("/my_socket/<username>") def my_socket(username): # 获取当前客户端和服务端的Socket 的连接 后面的type 是指定类型 提示信息 user_socket = request.environ.get("wsgi.websocket") # type : WebSocket if user_socket: user_socket_dict[username] = user_socket print(len(user_socket_dict), user_socket_dict) while True: msg = user_socket.receive() print(msg) for usocket in user_socket_dict.values(): # 异常处理 比如某一个断开了连接 try: usocket.send(msg) except: continue # return "200 okkk" @app.route("/gc") def gc(): return render_template("gc.html") if __name__ == '__main__': http_serve = WSGIServer(("0.0.0.0", 9988), app, handler_class=WebSocketHandler) http_serve.serve_forever()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>有名群聊</title> </head> <body> <p>我的昵称:<input type="text" id="username"></p><button onclick="logingc()">登录</button> <br> <p><input type="text" id="content"> <button onclick="send_msg()"> 发送消息</button></p> <div id="chat_list"></div> </body> <script type="application/javascript"> var ws = null ; function logingc(){ var username=document.getElementById("username").value; ws = new WebSocket("ws://192.168.16.60:9988/my_socket/"+username); // 监听 ws.onmessage = function(MessageEvent){ console.log(MessageEvent.data); str_obj=JSON.parse(MessageEvent.data); var p= document.createElement("p"); p.innerText =str_obj.from_user + ":" + str_obj.chat; document.getElementById("chat_list").appendChild(p); } } function send_msg(){ var username= document.getElementById("username").value; var content=document.getElementById("content").value; var sendstr={ from_user:username, chat:content, }; ws.send(JSON.stringify(sendstr)); } </script> </html>
私聊
字典形式
import json from flask import Flask, request, render_template # 提供WS协议服务 from geventwebsocket.handler import WebSocketHandler # 承载服务 from geventwebsocket.server import WSGIServer # 语法提示 from geventwebsocket.websocket import WebSocket from flask import Flask app = Flask(__name__) user_socket_dict = {} # 注意添加用户参数 @app.route("/my_socket/<username>") def my_socket(username): # 获取当前客户端和服务端的Socket 的连接 后面的type 是指定类型 提示信息 user_socket = request.environ.get("wsgi.websocket") # type : WebSocket if user_socket: user_socket_dict[username] = user_socket print(len(user_socket_dict), user_socket_dict) while True: msg = user_socket.receive() print(msg) msg_dict=json.loads(msg) to_user_nick=msg_dict.get("to_user") # print(to_user_nick) to_user_socket=user_socket_dict.get(to_user_nick) to_user_socket.send(msg) @app.route("/sl") def sl(): return render_template("sl.html") if __name__ == '__main__': http_serve = WSGIServer(("0.0.0.0", 9988), app, handler_class=WebSocketHandler) http_serve.serve_forever()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>私聊</title> </head> <body> <p>我的昵称:<input type="text" id="username"></p> <button onclick="logingc()">登录</button> <br> <br> <p>给<input type="text" id="to_user">发送 <input type="text" id="content"> <button onclick="send_msg()"> 发送消息</button> </p> <div id="chat_list"></div> </body> <script type="application/javascript"> var ws = null; function logingc() { var username = document.getElementById("username").value; ws = new WebSocket("ws://192.168.16.60:9988/my_socket/" + username); // 监听 ws.onmessage = function (MessageEvent) { console.log(MessageEvent.data); str_obj = JSON.parse(MessageEvent.data); var p = document.createElement("p"); p.innerText = str_obj.from_user + ":" + str_obj.chat; document.getElementById("chat_list").appendChild(p); } } function send_msg() { var username = document.getElementById("username").value; var to_user = document.getElementById("to_user").value; var content = document.getElementById("content").value; var sendstr = { from_user: username, to_user: to_user, chat: content, }; ws.send(JSON.stringify(sendstr)); } </script> </html>
jequery 版本
from flask import Flask, request, render_template from geventwebsocket.handler import WebSocketHandler from geventwebsocket.server import WSGIServer from geventwebsocket.websocket import WebSocket #注意添加 静态文件 位置 url 位置 app = Flask(__name__,static_folder='./statics/',static_url_path="/static") user_socket_list = [] @app.route("/test") def test(): user_socket = request.environ.get("wsgi.websocket") # type: WebSocket if user_socket: user_socket_list.append(user_socket) print(len(user_socket_list), user_socket_list) while True: msg = user_socket.receive() print(msg) for usocket in user_socket_list: try: usocket.send(msg) except: continue @app.route("/ww") def ww(): return render_template("ww.html") if __name__ == '__main__': http_server = WSGIServer(("0.0.0.0", 9988), app, handler_class=WebSocketHandler) http_server.serve_forever()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div>输入:<input type="text" id="content"> <button id="send">发送</button> </div> <div id='chat_list'></div> </body> #注意添加 js <script src="static/jquery-3.4.1.js"></script> <script type="application/javascript"> var ws = new WebSocket("ws://192.168.16.60:9988/test"); #注意 js 的用法 ws.onmessage = function (message) { console.log(message.data) var p = document.createElement("p"); $(p).text(message.data); $("#chat_list").append(p) } $("#send").click(function () { content = $("#content").val(); ws.send(content) }) </script> </html>