まず、WebSocketはHTML5関連で脚光を浴びている機能なので、知っている人も多いでしょう。簡単にいってしまえば、最初にHTTPプロトコルを偽装して、その後データのだだ漏れができるものです。Chromeでは去年サポートされ、Safariも今の最新版でサポートされています。iOS4.2.1でもサポートされているのでiPhoneからでも使えます。IE9では、「何それ?」の悲しい状態ですが、プロトコルの不安定さから仕方ないですね。Firefoxは4からサポートすると期待させておきながら、こんな残念な記事が今朝聞こえてきました。
WebSocketでデータの送受信をすると、Cometなどのロングポーリングよりはインフラに優しい作りになります。でも、ロングポーリングでも同じですが、アプリケーションサーバを複数立てたとき、アプリケーションサーバ間でデータをやりとりする必要がでてきます。リアルタイム性が大きなメリットなので、データベースとかは介せません。そこで有力なのがXMPPだと信じていますが、Google Waveが消えたのと、ブログの一つのエントリじゃ収まらないので、XMPPは忘れます。自前で作るとしたらハブサーバを作って、アプリケーションサーバとハブサーバの間でデータの垂れ流し(リレー)をさせるのが良さそうです。そのときのプロトコルもWebSocketで中身はJSONとか、MessagePackとかもいいかな、と思ったのですが、サンプルコードはライブラリを調べる時間も含めて30分以内に作らないといけないというローカルルールにより、行ベースで自分でソケットを開きます。
で、今回のサンプルは全部geventベースで書いています。非同期処理が同期処理のように書けるのがすごいメリットです。WSGIサーバにもなるので、FlaskでもDjangoでも使えます。で、なぜ、WebSocketを非同期にこだわるかはむか〜し、どこかで書いた気がするので省略。でも、今回のサンプルはある程度ブロックするので効率は悪いです。サンプルコードはここにあります。
まず、ハブサーバのコードです。
from gevent.server import StreamServer
class BCServer:
def __init__(self):
self.clients = []
def received(self, socket, addr):
fileobj = socket.makefile()
self.clients.append(fileobj)
try:
while True:
line = fileobj.readline()
if not line:
break
if line.startswith("quit:"):
break
if line.startswith("msg:"):
msg = line.split(":", 1)[1]
for fo in self.clients:
try:
fo.write(msg)
fo.flush()
except Exception, e:
print p
except Exception, e:
print e
finally:
self.clients.remove(fileobj)
if __name__ == "__main__":
bcs = BCServer()
server = StreamServer(("0.0.0.0", 3000), bcs.received)
server.serve_forever()
geventのsocketが標準ライブラリのsocketと全く同じようにあつかえます。メッセージを垂れ流しているだけなので、単純です。Webアプリケーションの方は、以前のコードとほとんど同じなので、ポイントだけ。全部読みたければ、これ。
chat_clients = set()
def read_server(fileobj):
while True:
data = fileobj.readline()
if not data:
print "end read_server"
break
for client in chat_clients:
client.send(data)
def handle_chat(ws):
chat_clients.add(ws)
while True:
message = ws.wait()
if message is None:
break
fileobj.write("msg:" + message + "\n")
fileobj.flush()
chat_clients.remove(ws)
fileobjはハブサーバに接続した後にsocket.makefileしたものです。これだけでメッセージをリレーしてくれます。えっ?信じられないって?それじゃ、動いている画像です。



