隨着互聯網的發展,傳統的HTTP協議已經很難知足Web應用日益複雜的需求了。近年來,隨着HTML5的誕生,WebSocket協議被提出,它實現了瀏覽器與服務器的全雙工通訊,擴展了瀏覽器與服務端的通訊功能,使服務端也能主動向客戶端發送數據。html
咱們知道,傳統的HTTP協議是無狀態的,每次請求(request)都要由客戶端(如 瀏覽器)主動發起,服務端進行處理後返回response結果,而服務端很難主動向客戶端發送數據;這種客戶端是主動方,服務端是被動方的傳統Web模式 對於信息變化不頻繁的Web應用來講形成的麻煩較小,而對於涉及實時信息的Web應用卻帶來了很大的不便,如帶有即時通訊、實時數據、訂閱推送等功能的應 用。在WebSocket規範提出以前,開發人員若要實現這些實時性較強的功能,常常會使用折衷的解決方法:輪詢(polling)和Comet技術。其實後者本質上也是一種輪詢,只不過有所改進。jquery
輪詢是最原始的實現實時Web應用的解決方案。輪詢技術要求客戶端以設定的時間間隔週期性地向服務端發送請求,頻繁地查詢是否有新的數據改動。明顯地,這種方法會致使過多沒必要要的請求,浪費流量和服務器資源。web
Comet技術又能夠分爲長輪詢和流技術。長輪詢改進了上述的輪詢技術,減少了無用的請求。它會爲某些數據設定過時時間,當數據過時後纔會向服務端發送請求;這種機制適合數據的改動不是特別頻繁的狀況。流技術一般是指客戶端使用一個隱藏的窗口與服務端創建一個HTTP長鏈接,服務端會不斷更新鏈接狀態以保持HTTP長鏈接存活;這樣的話,服務端就能夠經過這條長鏈接主動將數據發送給客戶端;流技術在大併發環境下,可能會考驗到服務端的性能。瀏覽器
這兩種技術都是基於請求-應答模式,都不算是真正意義上的實時技術;它們的每一次請求、應答,都浪費了必定流量在相同的頭部信息上,而且開發複雜度也較大。服務器
伴隨着HTML5推出的WebSocket,真正實現了Web的實時通訊,使B/S模式具有了C/S模式的實時通訊能力。WebSocket的工做流程是這 樣的:瀏覽器經過JavaScript向服務端發出創建WebSocket鏈接的請求,在WebSocket鏈接創建成功後,客戶端和服務端就能夠經過 TCP鏈接傳輸數據。由於WebSocket鏈接本質上是TCP鏈接,不須要每次傳輸都帶上重複的頭部數據,因此它的數據傳輸量比輪詢和Comet技術小 了不少。websocket
如下是 WebSocket 對象的屬性。假定咱們使用了以上代碼建立了 Socket 對象:併發
屬性 | 描述 |
---|---|
Socket.readyState | 只讀屬性 readyState 表示鏈接狀態,能夠是如下值:0 - 表示鏈接還沒有創建。1 - 表示鏈接已創建,能夠進行通訊。2 - 表示鏈接正在進行關閉。3 - 表示鏈接已經關閉或者鏈接不能打開。 |
Socket.bufferedAmount | 只讀屬性 bufferedAmount 已被 send() 放入正在隊列中等待傳輸,可是尚未發出的 UTF-8 文本字節數。 |
如下是 WebSocket 對象的相關事件。假定咱們使用了以上代碼建立了 Socket 對象:app
事件 | 事件處理程序 | 描述 |
---|---|---|
open | Socket.onopen | 鏈接創建時觸發 |
message | Socket.onmessage | 客戶端接收服務端數據時觸發 |
error | Socket.onerror | 通訊發生錯誤時觸發 |
close | Socket.onclose | 鏈接關閉時觸發 |
// 初始化一個 WebSocket 對象 var ws = new WebSocket("ws://localhost:9998/echo"); // 創建 web socket 鏈接成功觸發事件 ws.onopen = function () { // 使用 send() 方法發送數據 ws.send("發送數據"); alert("數據發送中..."); }; // 接收服務端數據時觸發事件 ws.onmessage = function (evt) { var received_msg = evt.data; alert("數據已接收..."); }; // 斷開 web socket 鏈接成功觸發事件 ws.onclose = function () { alert("鏈接已關閉..."); };
如下是 WebSocket 對象的相關方法。假定咱們使用了以上代碼建立了 Socket 對象:socket
方法 | 描述 |
---|---|
Socket.send() | 使用鏈接發送數據 |
Socket.close() | 關閉鏈接 |
websocket實現多人鏈接聊天ide
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .container{ border: 1px solid black; margin: auto 0; width: 600px; height: 300px; overflow: auto; } </style> </head> <body> <div style="width:700px;margin: 0 auto"> <h1>1024聊天室</h1> <div class="container"></div> <div class="button1"> <input type="text" id="txt"> <input type="button" value="發送" id="btn"> </div> </div> <script src="/static/jquery-3.3.1.js"></script> <script> $(function () { sendMessage(); }); ws=new WebSocket("ws://127.0.0.1:8888/chat"); //創建鏈接後執行 ws.onopen=function(){ }; // 接受服務端發過來的數據 ws.onmessage=function (event) { $(".container").append(event.data) }; //服務端主動關閉 ws.onclose=function () { }; function sendMessage(){ $("#btn").click(function () { ws.send($("#txt").val()); }) }; </script> </body> </html>
<div style="margin:10px;background-color: khaki ">{{client}}: {{msg}}</div>
from tornado.web import RequestHandler class indexhandler(RequestHandler): def get(self, *args, **kwargs): self.render("index.html")
from tornado.websocket import WebSocketHandler import uuid user=set() class ChatHandler(WebSocketHandler): #客戶端和服務端已經創建鏈接,會執行 def open(self, *args, **kwargs): print("來人了") user.add(self) # message 拿到客戶端發送的數據 def on_message(self, message): #render_string渲染模板,不send content=self.render_string("message.html",client=uuid.uuid4(),msg=message) for client in user: client.write_message(content) # message=message+"第十六屆兩個號多個" # 發送數據給客戶端 # self.write_message(message) def on_close(self): #客戶端主動關閉鏈接 user.remove(self)
from controller.index_request import indexhandler from controller.chat_websocket import ChatHandler import tornado.ioloop import tornado.web setting={ 'template_path':'templates', 'static_path':'static' } application=tornado.web.Application([ (r'/index',indexhandler), (r'/chat',ChatHandler) ],**setting) if __name__=="__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()