前言
websocket是HTML5出的一個新的網絡通信協議,與HTTP協議沒有關系。它們倆可以說是獨立的兩個協議,但是也會有一些共同點。
HTTP 協議是一種無狀態的、無連接的、單向的應用層協議。它采用了請求/響應模型。通信請求只能由客戶端發起,服務端對請求做出應答處理。
這種通信模型有一個弊端:HTTP 協議無法實現服務器主動向客戶端發起消息。
這種單向請求的特點,注定了如果服務器有連續的狀態變化,客戶端要獲知就非常麻煩。大多數 Web 應用程序將通過頻繁的異步JavaScript和XML(AJAX)請求實現長輪詢。輪詢的效率低,非常浪費資源(因為必須不停連接,或者 HTTP 連接始終打開)。

它是一個非持久化的協議,還有着有版本之分,有1.0,1.1,它的生命周期有Request來界定,在1.0版本中,一次請求,一次響應,那么這次請求就結束了。但是現在主流版本已經是1.1了,對於這種請求也有了改進,可以實現長連接,也就是說在一個HTTP當中可以發送多個請求,接收多個響應,但是一定要明確的一個點,必須一個請求對應一個響應,而且必須先請求,才能返回響應
websocket簡介
WebSocket 是 HTML5 開始提供的一種在單個 TCP 連接上進行全雙工通訊的協議。
websocket是一個基於HTTP的網絡協議,它和HTTP最主要的區別就是它比較持久
需要的庫
pip install flask
pip install gevent-websocket
聊天室功能實現
聊天室功能使用了flask來進行模板,url之類的解析,不同之處是不再使用flask自帶的容器,而是當作一個應用,被gevent里的一個uwsgiserver容器來調用
知識補充:
// 新建一個websocket鏈接 var s = new WebSocket("%s://%s/foobar/"); s.onopen = function() {} // 接收后端發送過來的數據 s.onmessage = function(e) {} s.onerror = function(e) {} // s.onclose = function(e) {} // 發送數據 s.send(value);
1、多人聊天室
后端:
from flask import Flask, request, render_template from geventwebsocket.websocket import WebSocket from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer import json app = Flask(__name__) @app.route("/index") def index(): return render_template("ws.html") user_socket_dict = {} @app.route("/ws/<username>") def ws(username): user_socket = request.environ.get("wsgi.websocket") # type: WebSocket if not user_socket: return "使用WebSocket方式連接" user_socket_dict[username] = user_socket print(user_socket_dict) while True: try: # 接收客戶端傳入數據 user_msg = user_socket.receive() # 將以用戶名為鍵,連接為值的字典迭代出所有的鍵值對 for k,v in user_socket_dict.items(): # 將當前發送數據的用戶以及數據放在一起組成字典 # print(v) who_send_msg = { "send_user": username, "send_msg": user_msg } # print(who_send_msg) # 如果當前連接與迭代出來的連接相同,就跳過本次循環 if user_socket == v: continue # 否則 將用戶以及用戶數據以json格式發送出去 v.send(json.dumps(who_send_msg)) except Exception as e: # 當捕捉到異常的時候就將當前用戶從字典中刪除 user_socket_dict.pop(username)if __name__ == '__main__': http_serv = WSGIServer(("0.0.0.0",8000),app,handler_class=WebSocketHandler) http_serv.serve_forever()
前端代碼:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <style> #chat_window { width: 450px; height: 450px; border: 1px solid black; border-radius: 20px; margin: 30px auto; } .span_div { margin: 25px; font-size: 18px; } .chat_span { font-size: 28px; border: 2px solid red; border-radius: 20px; } </style> </head> <body> <div class="container"> <h2 style="text-align: center">websocket聊天室</h2> <div class="row"> <div class="panel panel-info col-lg-6 col-lg-offset-3"> <div class="panel-heading">微信</div> <div class="panel-body"> <div style="text-align: center"> <label for="username">自報家門:</label> <input type="text" id="username"> <label for="to_username">發送目標:</label> <input type="text" id="to_username"> <button class="btn btn-danger" id="create_ws" onclick="go_to()">創建ws連接 </button> </div> <div id="chat_window"> </div> <div class="col-lg-12"> <div class="input-group"> <input type="text" class="form-control" id="send_msg"> <span class="input-group-btn"> <button class="btn btn-success" type="button" id="btn_send">發送</button> </span> </div><!-- /input-group --> </div><!-- /.col-lg-6 --> </div><!-- /.row --> </div> </div> </div> </div> </body> <script type="application/javascript"> var ws_url = "ws://127.0.0.1:8000/ws/"; var ws = null; function go_to() { var username = document.getElementById("username"); // 將用戶參數和url路徑一起組成新的url ws = new WebSocket(ws_url + username.value); console.log(ws); // 接收后端傳入的信息 ws.onmessage = function (serv_msg) { msg = JSON.parse(serv_msg.data); console.log(msg); creat_chat('y', msg); }; } // 添加聊天記錄 function creat_chat(self, content) { if (self == 'w') { self = 'right'; // 將自己的聊天記錄放入span標簽 var span_tag = document.createElement('span'); span_tag.innerText = content.send_msg; // 將用戶名放入span標簽 var span_tag1 = document.createElement('span'); span_tag1.innerText = ":我"; span_tag1.setAttribute('class', 'chat_span') } else { self = 'left'; // 將好友的聊天記錄放入span標簽 var span_tag = document.createElement('span'); span_tag.innerText = content.send_user + ":"; span_tag.setAttribute('class', 'chat_span'); // 將用戶名放入span標簽 var span_tag1 = document.createElement('span'); span_tag1.innerText = content.send_msg; } // 創建一個大的div標簽 var div_tag = document.createElement('div'); div_tag.setAttribute('class', 'span_div'); // 修改樣式 div_tag.style = 'text-align:' + self; // 將聊天記錄和用戶名添加進來 div_tag.appendChild(span_tag); div_tag.appendChild(span_tag1); var chat_window = document.getElementById('chat_window'); chat_window.appendChild(div_tag) } // 事件監聽 ,點擊發送按鈕要執行的代碼 document.getElementById("btn_send").addEventListener("click", function () { // 取出你輸入框里面的數據 var send_msg = document.getElementById("send_msg"); // 獲取發送目標標簽對象 var to_user = document.getElementById("to_username"); // 組成發送字典 send_msg_json = { send_msg: send_msg.value, to_user: to_user.value }; // 將字典進行序列化並發送 ws.send(JSON.stringify(send_msg_json)); var s_msg = {send_msg: send_msg.value}; creat_chat("w", s_msg); send_msg.value = '' }) </script> </html>
2、一對一聊天室
from flask import Flask, request, render_template from geventwebsocket.websocket import WebSocket from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer import json app = Flask(__name__) @app.route("/index2") def index(): return render_template("ws_one.html") user_socket_dict = {} @app.route("/ws/<username>") def ws(username): user_socket = request.environ.get("wsgi.websocket") # type: WebSocket if not user_socket: return "使用WebSocket方式連接" user_socket_dict[username] = user_socket while True: try: # 接收客戶端傳入數據 user_msg = user_socket.receive() user_msg = json.loads(user_msg) to_user_socket = user_socket_dict.get(user_msg.get("to_user")) # 將當前發送數據的用戶以及數據放在一起組成字典 send_msg = { "send_user": username, "send_msg": user_msg.get("send_msg") } to_user_socket.send(json.dumps(send_msg)) except WebSocketHandler as e: # 當捕捉到異常的時候就將當前用戶從字典中刪除 user_socket_dict.pop(username) print(e) print(user_socket_dict) if __name__ == '__main__': http_serv = WSGIServer(("0.0.0.0",8000),app,handler_class=WebSocketHandler) http_serv.serve_forever()
前端代碼:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <style> #chat_window { width: 450px; height: 450px; border: 1px solid black; border-radius: 20px; margin: 30px auto; } .span_div { margin: 25px; font-size: 18px; } .chat_span { font-size: 28px; border: 2px solid red; border-radius: 20px; } </style> </head> <body> <div class="container"> <h2 style="text-align: center">websocket聊天室</h2> <div class="row"> <div class="panel panel-info col-lg-6 col-lg-offset-3"> <div class="panel-heading">微信</div> <div class="panel-body"> <div style="text-align: center"> <label for="username">自報家門:</label> <input type="text" id="username"> <label for="to_username">發送目標:</label> <input type="text" id="to_username"> <button class="btn btn-danger" id="create_ws" onclick="go_to()">創建ws連接 </button> </div> <div id="chat_window"> </div> <div class="col-lg-12"> <div class="input-group"> <input type="text" class="form-control" id="send_msg"> <span class="input-group-btn"> <button class="btn btn-success" type="button" id="btn_send">發送</button> </span> </div><!-- /input-group --> </div><!-- /.col-lg-6 --> </div><!-- /.row --> </div> </div> </div> </div> </body> <script type="application/javascript"> var ws_url = "ws://127.0.0.1:8000/ws/"; var ws = null; function go_to() { var username = document.getElementById("username"); // 將用戶參數和url路徑一起組成新的url ws = new WebSocket(ws_url + username.value); console.log(ws); // 接收后端傳入的信息 ws.onmessage = function (serv_msg) { msg = JSON.parse(serv_msg.data); console.log(msg); creat_chat('y', msg); }; } // 添加聊天記錄 function creat_chat(self, content) { if (self == 'w') { self = 'right'; // 將自己的聊天記錄放入span標簽 var span_tag = document.createElement('span'); span_tag.innerText = content.send_msg; // 將用戶名放入span標簽 var span_tag1 = document.createElement('span'); span_tag1.innerText = ":我"; span_tag1.setAttribute('class', 'chat_span') } else { self = 'left'; // 將好友的聊天記錄放入span標簽 var span_tag = document.createElement('span'); span_tag.innerText = content.send_user + ":"; span_tag.setAttribute('class', 'chat_span'); // 將用戶名放入span標簽 var span_tag1 = document.createElement('span'); span_tag1.innerText = content.send_msg; } // 創建一個大的div標簽 var div_tag = document.createElement('div'); div_tag.setAttribute('class', 'span_div'); // 修改樣式 div_tag.style = 'text-align:' + self; // 將聊天記錄和用戶名添加進來 div_tag.appendChild(span_tag); div_tag.appendChild(span_tag1); var chat_window = document.getElementById('chat_window'); chat_window.appendChild(div_tag) } // 事件監聽 ,點擊發送按鈕要執行的代碼 document.getElementById("btn_send").addEventListener("click", function () { // 取出你輸入框里面的數據 var send_msg = document.getElementById("send_msg"); var to_user = document.getElementById("to_username"); send_msg_json = { send_msg:send_msg.value, to_user:to_user.value }; ws.send(JSON.stringify(send_msg_json)); var s_msg = {send_msg: send_msg.value}; creat_chat("w", s_msg); send_msg.value = '' }) </script> </html>