2012年5月25日 星期五

[html5][gae]Server-Side Event

在 html5 的規格中,有看到一個 Server-Side Event (縮寫 SSE),這是什麼東西呢?是不是我最愛的伺器器主動通知呢?我好奇看了一下 w3schools 的說明,著實令我心動啊。

A server-sent event is when a web page automatically gets updates from a server.

This was also possible before, but the web page would have to ask if any updates were available. With server-sent events, the updates come automatically.

Examples: Facebook/Twitter updates, stock price updates, news feeds, sport results, etc.

這麼多大公司都用了,那我就可以放心的用吧。我們先來看怎麼用,再來講講我的感想吧。

因為我的環境是 Google App Engine,那麼我們就來改寫 w3schools 的範例吧。原來的網頁的程式碼,長這樣:

<!DOCTYPE html>
<html>
<body>
<h1>Getting server updates</h1>
<div id="result"></div>

<script>
if(typeof(EventSource)!=="undefined")
  {
  var source=new EventSource("demo_sse.php");
  source.onmessage=function(event)
    {
    document.getElementById("result").innerHTML+=event.data + "<br />";
    };
  }
else
  {
  document.getElementById("result").innerHTML="Sorry, your browser does not support server-sent events...";
  }
</script>

</body>
</html>

我們只要把 var source=new EventSource("demo_sse.php") 換成 var source=new EventSource("demo_sse")。整個網頁的 javascript 的精神就是,有 onmessage 事件發生的時候,就把事件裡的 data 值取出來,加到 id 為 result 的元素的內容去。所以改完變成:

<!DOCTYPE html>
<html>
<body>
<h1>Getting server updates</h1>
<div id="result"></div>

<script>
if(typeof(EventSource)!=="undefined")
  {
  var source=new EventSource("time");
  source.onmessage=function(event)
    {
    document.getElementById("result").innerHTML+=event.data + "<br />";
    };
  }
else
  {
  document.getElementById("result").innerHTML="Sorry, your browser does not support server-sent events...";
  }
</script>

</body>
</html>

我把上面的內容存成 main.html,放在跟 main.py 同一個目錄下。然後,main.py 的內容修改一下,在 MainHandler 的 get 方法,就改成直讀 main.html 在寫出到 out。

class MainHandler(webapp.RequestHandler):
    def get(self):
        path = os.path.join(os.path.dirname(__file__), 'main.html')
        render_content = codecs.open(path, 'r', encoding='utf8').read()
        self.response.out.write(render_content)

因為網頁會送出 /time 的要求,所以要處理他,必須要接受他。所以在 main() 要修改多一個處理

def main():
    application = webapp.WSGIApplication([('/', MainHandler),
                                          ('/time', TimeHandler),
                                          ],
                                         debug=True)

接下來寫一個 TimeHandler 接收來自網頁的要求,

class TimeHandler(webapp.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/event-stream'
        self.response.headers['Cache-Control'] = 'no-cache'
        p = "data: " + str(datetime.datetime.now()) + "\n\n"
        self.response.out.write(p)
    def post(self):
        pass

好,這樣就行了。試跑看看是不是有新的時間一直新增呢?

image

接下來是感想時間。

這個 Server-Side Event,我原本以為需要很新的 http Server 程式,例如新的 apache 或新的 IIS 或等等的。其實不用。看我的 TimeHandler 就知道,只要你會改 response 的 Content-Type 及 Cache-Control 的內容,就可以使用這個功能。一點也不難。要用得很難也行,進階版的用法請看 html5rocks

再來,他真的是 Server 主動通知嗎?,讓我們來看看網路活動吧,打開 chrome 的檢查元素,點網路。

image

會發現到,每隔幾秒就會有網路活動,從瀏覽器送出來。有寫過的人就會想說,這我自己用 interval 或 timer 寫不就一樣?對,我也覺得一樣。既然一樣,那用哪個都好,html5 提供了這個功能,讓大家不用傷腦筋去管理 timer,也是有方便之處。也同時提醒一下不清楚的人,不要以為這就是伺服器主動通知的事件,這實際上還是 client 端 polling 的方法。

最後啊,我要提醒兩下,

(1)這個 text/event-stream 一律使用 utf-8 編碼,跟 json 一模一樣,所以中文的朋友一定要先轉好。

(2)看文件真的不是一件容易的事,尤其規格的文件這麼大一份,參考來參考去的。越是低階的規格就越大本,我從來不知道在哪裡可以找到正式文件說,遇到兩個換行,就代表傳送結束,所以,如果你真要結束,就一定要連續傳兩個換行。如果你不想要結束,傳送內容前,一定要檢查有沒有兩個換行。當然,有些 http server 也許會幫你加兩個換行在後面。如果出問題,請記得你的內容,有沒有兩個換行在你不想要的地方。

沒有留言:

張貼留言