HTML5 WebSocket

1、HTTP協議javascript

  傳統的HTTP協議是無狀態的,每次請求(request)都要由客戶端(如 瀏覽器)主動發起,服務端進行處理後返回response結果,而服務端很難主動向客戶端發送數據;html

這種客戶端是主動方,服務端是被動方的傳統Web模式。 對於信息變化不頻繁的Web應用來講形成的麻煩較小,而對於涉及實時信息的Web應用卻帶來了很大的不便,java

如帶有即時通訊、實時數據、訂閱推送等功能的應 用。在WebSocket規範提出以前,開發人員若要實現這些實時性較強的功能,常常會使用折衷的解決方法:web

  輪詢(polling)Comet技術。其實後者本質上也是一種輪詢,只不過有所改進。後端

  輪詢是最原始的實現實時Web應用的解決方案。輪詢技術要求客戶端以設定的時間間隔週期性地向服務端發送請求,頻繁地查詢是否有新的數據改動。明顯地,這種方法會致使過多沒必要要的請求,浪費流量和服務器資源。輪詢是在特定的的時間間隔(如每1秒),由瀏覽器對服務器發出HTTP請求,而後由服務器返回最新的數據給客戶端的瀏覽器。這種傳統的模式帶來很明顯的缺點,即瀏覽器須要不斷的向服務器發出請求,然而HTTP請求可能包含較長的頭部,其中真正有效的數據可能只是很小的一部分,顯然這樣會浪費不少的帶寬等資源。瀏覽器

  Comet技術又能夠分爲長輪詢流技術長輪詢改進了上述的輪詢技術,減少了無用的請求。它會爲某些數據設定過時時間,當數據過時後纔會向服務端發送請求;這種機制適合數據的改動不是特別頻繁的狀況。流技術一般是指客戶端使用一個隱藏的窗口與服務端創建一個HTTP長鏈接,服務端會不斷更新鏈接狀態以保持HTTP長鏈接存活;這樣的話,服務端就能夠經過這條長鏈接主動將數據發送給客戶端;流技術在大併發環境下,可能會考驗到服務端的性能。服務器

  這兩種技術都是基於請求-應答模式,都不算是真正意義上的實時技術;它們的每一次請求、應答,都浪費了必定流量在相同的頭部信息上,而且開發複雜度也較大。websocket

2、WebSocket併發

  WebSocket 是 HTML5 開始提供的一種在單個 TCP 鏈接上進行全雙工通信的協議。app

  WebSocket 使得客戶端和服務器之間的數據交換變得更加簡單,容許服務端主動向客戶端推送數據。在 WebSocket API 中,瀏覽器和服務器只須要完成一次握手,二者之間就直接能夠建立持久性的鏈接,並進行雙向數據傳輸。

  在 WebSocket API 中,瀏覽器和服務器只須要作一個握手的動做,而後,瀏覽器和服務器之間就造成了一條快速通道。二者之間就直接能夠數據互相傳送。

HTML5 定義的 WebSocket 協議,能更好的節省服務器資源和帶寬,而且可以更實時地進行通信。

瀏覽器經過 JavaScript 向服務器發出創建 WebSocket 鏈接的請求,鏈接創建之後,客戶端和服務器端就能夠經過 TCP 鏈接直接交換數據。

當你獲取 Web Socket 鏈接後,你能夠經過 send() 方法來向服務器發送數據,並經過 onmessage 事件來接收服務器返回的數據。

 

爲客戶端推送時間消息的 Tornado WebSocket程序:

import tornado.ioloop
import tornado.web
import tornado.websocket
import threading
import time
import datetime
import asyncio


from tornado.options import define, options, parse_command_line
define("port", default=8888, help="run on the given port", type=int)
clients = dict()                        # 客戶端Session 字典


class IndexHandler(tornado.web.RequestHandler):

    async def get(self):
        self.render("index.html")


class MyWebSocketHandler(tornado.websocket.WebSocketHandler):

    def open(self, *args):                         # 有新連接時被調用
        self.id = self.get_argument("Id")
        # 保存Session 到 clients字典中
        clients[self.id] = {"id": self.id, "object": self}
        print("open function", str(clients))

    def on_message(self, message):              # 接受消息時被調用
        print("Client %s received a message : %s" % (self.id, message))

    def on_close(self):                            # 關閉連接時被調用
        if self.id in clients:
            del clients[self.id]
            print("Client %s is closed" % self.id)

    def check_origin(self, origin):
        return True


app = tornado.web.Application([(r'/', IndexHandler),(r'/websocket', MyWebSocketHandler)])

# 啓動單獨的線程運行此函數, 每隔一秒向全部的客戶端推送當前時間
def sendTime():
    asyncio.set_event_loop(asyncio.new_event_loop())   # 啓動異步 event loop
    while True:
        for key in clients.keys():
            msg = str(datetime.datetime.now())
            clients[key]["object"].write_message(msg)
            print("write to client %s: %s" % (key, msg))
        time.sleep(1)


if __name__ == "__main__":
    threading.Thread(target=sendTime).start()
    parse_command_line()
    app.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()        # 掛起運行

##############################################################################################
  • 定義了全局變量字典clients, 用於保存全部與服務器創建起WebSocket連接的客戶端信息。字典的鍵是客戶端的id, 值是一個由id與相應的WebSocketHandler實例構成的元組;
  • IndexHandler 是一個普通的頁面處理器,用於向客戶端渲染主頁, 該頁面中包含了 Websocket 的客戶端程序;
  • MyWebSockerHandler 是本例的核心處理器,繼承 tornado.web.WebSocketHandler. 其中 open() 函數將全部客戶端連接保存到 clients字典中;
  • on_message() 函數用於顯示客戶端發來的消息; on_close()函數用於將已經關閉的 WebSocket連接從 clients 字典中移除。
  • 函數 sendTime() 運行在單獨的線程中,每隔一秒輪詢 clients 中的全部客戶端並經過 MyWebSocketHandler.write_message() 函數向客戶端推送時間消息。
  • 全部 Tornado 線程中必須有一個 event_loop, 該項要求經過 sendTime() 函數中的第一行代碼被知足。
  • 本例的 tornado.web.Application 實例中只配置了兩個路由,分別指向 IndexHandler 和 MyWebSocketHandler, 仍然由 Tornado IOLoop 啓動並運行。
 
 
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Python後端WebSocket的實現</title>
</head>
<body>
    <a href="javascript:WebSocketTest()">Run Websocket</a>
    <div id="messages" style="height:200px; background:black; color: white;"></div>
    <script type="text/javascript">
        var messageContainer = document.getElementById("messages");
        function WebSocketTest(){

            // 判斷當前瀏覽器是否支持WebSocket
            if("WebSocket" in window){
                messageContainer.innerHTML = "WebSocket is supported by your browser!";
                let ws = new WebSocket("ws://localhost:8888/websocket?Id=666888");

                //鏈接成功創建的回調方法
                ws.onopen = function(){
                    ws.send("Message to send");
                };

                //接收到消息的回調方法
                ws.onmessage = function(evt){
                    var received_msg = evt.data;
                    messageContainer.innerHTML += "<br/>Message is received: " + received_msg;
                };

                //鏈接關閉的回調方法
                ws.onclose = function(){
                    messageContainer.innerHTML += "<br/>Connection is closed ...";
                };
            }else{
                messageContainer.innerHTML = "WebSocket Not Supported by your Browser!";
            }
        }
    </script>
</body>
</html>

WebSocket程序運行效果:

相關文章
相關標籤/搜索