ファイナンス、情報通信技術のスキル・アグリゲーション・サイト

' . iseeit.jp 情報通信技術 . '

HTML5 Server-Sent Events サンプルプログラム(Python Tornado版・PHP版)

HTML5 Server-Sent Events( SSE、EventSource)のサンプルプログラムです。サーバ側が Python Tornado 版と PHP 版です。

サンプルプログラムでは、およそ 10 秒間隔でサーバ時刻を送信して、受信したブラウザ上でその時刻を更新します。

メッセージのフィールドについてです。定義されているフィールドは次の通りです。

  • event :イベントのタイプです。イベントはブラウザ内で、イベント名に応じたイベントリスナへ送られます。デフォルトの値は "message" です。
  • data :メッセージのデータフィールドです。
  • id :イベント ID です。
  • retry :メッセージの送信間隔です。ミリ秒単位で指定します。

メッセージは、UTF-8 を用いてエンコードされなければなりません。メッセージは、2つの改行文字で区切られます。

メッセージヘッダの Content-Type には、 text/event-stream を設定します。

ブラウザ側のスクリプトについてです。

EventSource オブジェクトを作成して、イベントを受け取るためにサーバへの接続を開始します。

addEventListener() を使用して対象のイベントメッセージを待ち受けます。

onerror コールバックは、エラーイベントを受けます。

Microsoft Internet Explorer は、Server-Sent Events をサポートしていません。

Server-Sent Events の Python Tornado プログラム例



#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.httpserver
import tornado.ioloop
import tornado.web
import os
from datetime import datetime


class MainHandler(tornado.web.RequestHandler):
  def get(self):
    self.render("index.html")

class SSEHandler(tornado.web.RequestHandler):
  @tornado.gen.coroutine
  def get(self):
    self.set_header("Content-Type", "text/event-stream")
    self.set_header("Cache-Control", "no-cache")

    yield self.write("event: messages\n")
    value = datetime.now().strftime("%H:%M:%S")
    yield self.write("data: %s\n" % value.encode( "utf-8" ))
    yield self.write("retry: 10000\n")
    yield self.write("\n")
    yield self.flush()

settings = {
  "template_path": os.path.join(os.path.dirname(__file__), "templates"),
} 

application = tornado.web.Application([
  (r"/sse", SSEHandler),
  (r"/", MainHandler),
], **settings)

if __name__ == "__main__":
  http_server = tornado.httpserver.HTTPServer(
    application, ssl_options={
    "certfile": "server.crt",
    "keyfile":  "server.key",})
  http_server.listen(8888)
  tornado.ioloop.IOLoop.instance().start()

プログラム例のテンプレートは、Pyhton プログラムを保存したディレクトリに templates ディレクトリを作成して index.html のファイル名で保存します。



<!DOCTYPE html>
<html>
 <head>
  <meta charset="UTF-8">
  <title>Tornado SSE Demo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <script>
function sse() {
 if (typeof (EventSource) !== "undefined") {
  var source = new EventSource("/sse");
  source.addEventListener("messages", function(event){
   document.getElementById("msg").innerHTML = "<p>" + event.data + "</p>" ;
  },false);

  source.onerror = function (event) {
   if (source.readyState === EventSource.CLOSED) {
  document.getElementById("msg").innerHTML = "<p>終了しています。</p>" ;
   }
   else if (source.readyState === EventSource.OPEN) {
    document.getElementById("msg").innerHTML = "<p>終了します。</p>" ;
    source.close();
   }
  }        
 } else {
  document.getElementById("msg").innerHTML = "<p>Server-Sent Events はサポートされていません。</p>" ;
 }
}
  </script>
 </head>
 <body>
  <p>Message</p>
  <div id="msg" style="border: 1px solid black">
  </div>
  <script>
window.onload = function() {
 sse();
};
  </script>
 </body>
</html>

Server-Sent Events の Python Bottle プログラム例(参考)

さて、参考として、 Bottle フレームワークでのプログラム例です。 apache httpd + mod_wsgi 環境で実行します。



#!/usr/bin/env python
# -*- coding:utf-8 -*-
from bottle import get, post, route, view, request, response, redirect, Bottle
from datetime import datetime

app = Bottle(__name__)

@app.get("/sse")
def sse():

  response.content_type  = "text/event-stream"
  response.cache_control = "no-cache"

  yield "event: messages\n"
  value = datetime.now().strftime("%H:%M:%S")
  yield "data: %s\n" % value.encode( "utf-8" )
  yield "retry: 10000\n"
  yield "\n"

@app.route("/")
@view("index")
def index():
  return 

if __name__ == "__main__":
  app.run()

テンプレートは、Tornado プログラム例の index.html をそのまま使用しますが、Pyhton プログラムのあるディレクトリに views ディレクトリを作成して保存します。

adapter.wsgi ファイルの例です。Pyhton プログラム(bsse.py とします)と同じディレクトリに保存します。



#!/usr/bin/env python
import sys, os ,bottle

os.environ["LANG"] = "ja_JP.UTF-8"

dirpath = os.path.dirname(os.path.abspath(__file__))
sys.path.append(dirpath)
os.chdir(dirpath)

from bsse import app
application = app

httpd.conf の VirtualHost などに追加する設定例です。



WSGIDaemonProcess wsgi-sse user=example group=example processes=1 threads=5 python-path=/usr/local/lib/python2.7/site-packages
                                          
WSGIScriptAlias / /home/example/sse/adapter.wsgi
<Directory /home/example/sse>
  WSGIProcessGroup wsgi-sse
  WSGIApplicationGroup %{GLOBAL}
  Order deny,allow
  Allow from all
 </Directory>

Server-Sent Events の PHP プログラム例( event フィールドあり)

event フィールドがあるメッセージの場合です。



<?php
header("Content-Type: text/event-stream");
header("Cache-Control: no-cache");
  echo "event: " . "messages" . PHP_EOL;
  echo "data: " . date(H:i:s", time()) . PHP_EOL;
  echo "retry: 10000" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
?>

プログラム例では、ウェブサーバのドキュメントルートに PHP プログラムを sse.php のファイル名で保存し、HTML ファイルも保存します。



<!DOCTYPE html>
<html>
 <head>
  <meta charset="UTF-8">
  <title>PHP SSE Demo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <script>
function sse() {
 if (typeof (EventSource) !== "undefined") {
  var source = new EventSource("sse.php");
  source.addEventListener("messages", function(event){
   document.getElementById("msg").innerHTML = "<p>" + event.data + "</p>" ;
  },false);

  source.onerror = function (event) {
   if (source.readyState === EventSource.CLOSED) {
    document.getElementById("msg").innerHTML = "<p>終了しています。</p>" ;
   }
   else if (source.readyState === EventSource.OPEN) {
    document.getElementById("msg").innerHTML = "<p>終了します。</p>" ;
    source.close();
   }
  }        
 } else {
  document.getElementById("msg").innerHTML = "<p>Server-Sent Events はサポートされていません。</p>" ;
 }
}
  </script>
 </head>
 <body>
  <p>Message</p>
  <div id="msg" style="border: 1px solid black">
  </div>
  <script>
window.onload = function() {
  sse();
};
  </script>
 </body>
</html>

Server-Sent Events の PHP プログラム例( event フィールドなし)

次に、 event フィールドのないメッセージの場合です。このとき、デフォルトの "message" が設定されます。



<?php
header("Content-Type: text/event-stream");
header("Cache-Control: no-cache");
  echo "data: " . date("H:i:s", time()) . PHP_EOL;
  echo "retry: 10000" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
?>

addEventListener() を使ってイベント "message" を待ち受けることもできますが、この場合、onmessage コールバックを使うこともできます。



<!DOCTYPE html>
<html>
 <head>
  <meta charset="UTF-8">
  <title>Tornado SSE Demo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <script>
function sse() {
 if (typeof (EventSource) !== "undefined") {
  var source = new EventSource("sse.php");
  source.onmessage = function (event){
   document.getElementById("msg").innerHTML = "<p>" + event.data + "</p>" ;
  }

  source.onerror = function (event) {
   if (source.readyState === EventSource.CLOSED) {
    document.getElementById("msg").innerHTML = "<p>終了しています。</p>" ;
   }
   else if (source.readyState === EventSource.OPEN) {
    document.getElementById("msg").innerHTML = "<p>終了します。</p>" ;
    source.close();
   }
  }        
 } else {
  document.getElementById("msg").innerHTML = "<p>Server-Sent Events はサポートされていません。</p>" ;
 }
}
  </script>
 </head>
 <body>
  <p>Message</p>
  <div id="msg" style="border: 1px solid black">
  </div>
  <script>
window.onload = function() {
 sse();
};
  </script>
 </body>
</html>

ファイナンシャル・プランニング
6つの係数

終価係数 : 元本を一定期間一定利率で複利運用したとき、将来いくら になるかを計算するときに利用します。

現価係数 : 将来の一定期間後に目標のお金を得るために、現在いくら の元本で複利運用を開始すればよいかを計算するときに利用します。

年金終価係数 : 一定期間一定利率で毎年一定金額を複利運用で 積み立て たとき、将来いくら になるかを計算するときに利用します。

年金現価係数 : 元本を一定利率で複利運用しながら、毎年一定金額を一定期間 取り崩し ていくとき、現在いくら の元本で複利運用を開始すればよいかを計算するときに利用します。

減債基金係数 : 将来の一定期間後に目標のお金を得るために、一定利率で一定金額を複利運用で 積み立て るとき、毎年いくら ずつ積み立てればよいかを計算するときに利用します。

資本回収係数 : 元本を一定利率で複利運用しながら、毎年一定金額を一定期間 取り崩し ていくとき、毎年いくら ずつ受け取りができるかを計算するときに利用します。

積み立て&取り崩しモデルプラン

積立金額→年金額の計算 : 年金終価係数、終価係数、資本回収係数を利用して、複利運用で積み立てた資金から、将来取り崩すことのできる年金額を計算します。

年金額→積立金額の計算 : 年金現価係数、現価係数、減債基金係数を利用して、複利運用で将来の年金プランに必要な資金の積立金額を計算します。