主にプログラミングに関して。Python, .NET Framework(C#), JavaScript, その他いくらか。
記事にあるサンプルやコードは要検証。使用に際しては責任を負いかねます

スポンサーサイト

                
tags:
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Python, Tornado: 二つの公式チャットデモ(WebSocket)

                
 Tornadoの公式のコードの中に、demosというディレクトリがあって、そこに二種類のチャット実装デモがある。
https://github.com/tornadoweb/tornado/tree/master/demos

 従来のHTTPで考えると、サーバから能動的にクライアントになにかを投げることはできず、つねにクライアントからのリクエストに応じてレスポンスを返すことになる。その条件でチャットを実装しようとすると、クライアントから定期的にリクエストを送る必要がある。chatというディレクトリに入っているデモでは、JavaScriptで500msごとにタイマーをかけてリクエストを行うことで実装している。ここら辺が旧来の技術で、ロングポールと呼ばれる一つの手法。

 定期的にリクエストを送って、サーバで新しいメッセージがないかを確認してレスポンスを作る。なんか非合理。サーバからクライアントに能動的にデータを送れたら合理的。というわけで新しい技術標準として開発が進められたのがWebSocket。このWebSocketを利用してチャットを実装したのが、デモの中でwebsocketというディレクトリに入っているもの。どう使っているのか気になるのでコードを読んでみる。

 まず予習。そもそもWebSocketがクライアントとサーバの双方向通信を可能にするものだとして、どう使えばいいんだろう。基本的な使いかたから。
 WebSocketはメッセージベースの通信を行う。送受信データの形式はテキスト、あるいはバイナリ。接続の確立、メッセージの送受信、接続の終了を機能としていてけっこうシンプル。
サーバ側(Python, Tornado):https://sites.google.com/site/tornadowebja/documentation/integration-with-other-services/tornado-websocket
class EchoWebSocket(websocket.WebSocketHandler):
def open(self):
print "WebSocket opened"

def on_message(self, message):
self.write_message(u"You said: " + message)

def on_close(self):
print "WebSocket closed"

クライアント側:http://www.html5rocks.com/ja/tutorials/websockets/basics/
var ws = new WebSocket("ws://localhost:8888/websocket");
ws.onopen = function() {
ws.send("Hello, world");
};
ws.onmessage = function (evt) {
alert(evt.data);
};

要は接続とメッセージの送受信をどうするかを規定のメソッドで定義すればいいんだろう。

 デモのコードを読む。まずクライアント側から。websocket/static/chat.jsがそのファイル。そんでもってその中で定義されているオブジェクトupdaterがWebSocketをメンバーに入れて通信処理をしている。showMessage メソッドを定義しておいて、WebSocketのonmessageイベントのときに呼ばれるようにしている。
var updater = {
socket: null,

start: function() {
var url = "ws://" + location.host + "/chatsocket";
updater.socket = new WebSocket(url);
updater.socket.onmessage = function(event) {
updater.showMessage(JSON.parse(event.data));
}
},

showMessage: function(message) {
var existing = $("#m" + message.id);
if (existing.length > 0) return;
var node = $(message.html);
node.hide();
$("#inbox").append(node);
node.slideDown();
}
};


 サーバ側。
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html", messages=ChatSocketHandler.cache)

class ChatSocketHandler(tornado.websocket.WebSocketHandler):
waiters = set()
cache = []
cache_size = 200

def allow_draft76(self):
# for iOS 5.0 Safari
return True

def open(self):
ChatSocketHandler.waiters.add(self)

def on_close(self):
ChatSocketHandler.waiters.remove(self)

@classmethod
def update_cache(cls, chat):
cls.cache.append(chat)
if len(cls.cache) > cls.cache_size:
cls.cache = cls.cache[-cls.cache_size:]

@classmethod
def send_updates(cls, chat):
logging.info("sending message to %d waiters", len(cls.waiters))
for waiter in cls.waiters:
try:
waiter.write_message(chat)
except:
logging.error("Error sending message", exc_info=True)

def on_message(self, message):
logging.info("got message %r", message)
parsed = tornado.escape.json_decode(message)
chat = {
"id": str(uuid.uuid4()),
"body": parsed["body"],
}
chat["html"] = tornado.escape.to_basestring(
self.render_string("message.html", message=chat))

ChatSocketHandler.update_cache(chat)
ChatSocketHandler.send_updates(chat)

"/"にリクエストを送ると、これまでのチャットメッセージがテンプレーティングされたHTMLが返ってくる。MainHandlerクラスでそのレスポンスを作っていて、これまでのメッセージはクラスメソッド(インスタンスを作らずクラスから使えるメソッド)に保存されている。テンプレーティングされたHTMLにさきほどのJavaScriptが入っており、読み込まれたらそれ以降でクライアントとサーバのWebSocket通信が確立される。
クライアント サーバ
→ チャットページへのリクエスト
← WebSocketでの接続コードを含むHTMLをレスポンスで返す
→ 読み込まれたJavaScriptからWebSocketでサーバへ接続要請
:サーバが接続要請を受けて接続を確立、以降双方向通信が可能に
 クラスメソッドを通じてこれまでのメッセージ、接続クライアントを保持している。ここらが重要。クライアントからの接続をなんらかの方法で保持しておかないと、メッセージのプッシュができない。HTTPリクエストとは違って、レスポンスを返したら接続を切っていいわけではない。接続保持の方法としてリクエストハンドラのインスタンスを安直にグローバル変数に入れることもできるだろうが、ここではクラスメソッドを使ってクラス変数へ入れている。


 WebSocketのTornadoでの使用方法をのぞいてみた。クライアント側ではWebSocketオブジェクトを作成して、onopen、onmessageイベントを書いておく。Tornadoサーバ側ではtornado.websocket.WebSocketHandlerクラスを継承してopenメソッドやon_messageメソッドを定義しておく、接続インスタンスを保持しておく。このあたりが使い方の根本的な部分だと考えられる。
            

コメントの投稿

非公開コメント

プロフィール

hMatoba

Author:hMatoba
Github

最新記事
リンク
作ったものなど
月別アーカイブ
カテゴリ
タグリスト

検索フォーム
Amazon
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。