2009年12月24日木曜日

client part

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
んで、前々回のエントリのクライアントサイド。submitでreturn falseして、それ以上のsubmit動作を抑制しなきゃいけないことを忘れていたのは公然の秘密。

<html>
<head>
<title>comet tests</sample>
<!--
Copyright 2009 Noriyuki Hosaka bgnori@gmail.com
-->

<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("jquery", "1.3.2");
</script>

<script type="text/javascript">
function update(){
$.ajax({
url: "http://localhost:3165",
dataType : "jsonp",
type: "GET",
cache : false,
data : {},
timeout: 60*1000,
success : function (data, dataType){
$("#chat").append($("<div>" + data.who + ":" + data.message + "</div>"));
update();
},
error : function(){
alert("error");
}
});
};

$(document).ready(function(){
$("form").submit(function(){
d = $('#message').val()
$.ajax({
url:"http://localhost:3124",
dataType : "jsonp",
//type: "POST",
type: "GET",
cache : false,
data : {message:d},
timeout: 60*1000,
success : function (data, dataType){
$('#message').val('');
},
error : function(){
alert("error");
},
});
return false;
});
$("#chat").append($("<div>this is chat area.</div>"));
update();
});
</script>

</head>
<div id="chat">
<div> 0 </div>
</div>

<form >
<input type="text" id="message" />
<input type="submit" />
<input type="reset" />
</form>
<body>
</body>
</html>

chrome for linux

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
LSBのバージョンが3.2である必要があるといわれたが、CentOSでは3.1でしかないので無理矢理いれてみた。

[nori@shinano]~% sudo rpm -U --force --nodeps google-chrome-beta_current_x86_64.rpm


ランチャのプロパティを見ると起動のpathが

/opt/google/chrome/google-chrome %U

になっているが、そんなものは無い。orz

pythonでとりあえずcometしてみた。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
標準のWSGI refのsimple serverからかっぱいできたhandlerを無理矢理くっつけてみた。
グチャグチャなのだが、恥を忍んで公開。

コアとなるアイディアは、標準のHTTPHandlerはsocketをfileとして読み書きするので、そのファイルをStringIOで差し替え、Protocol:dataReceivedで受け取ったdataをStringIOインスタンス(rfile)にどんどん書いてからHTTPHandlerに渡す。responseに関してはその逆をする(wfile)。


def connectionMade(self):
print 'Subscription::connectionMade'
self.rfile = StringIO.StringIO()#'rb', self.rbufsize)
self.wfile = StringIO.StringIO()#'wb', self.wbufsize)
self.adaptor = WSGIAdaptor(('localhost', 3165), WSGISupportHandler) #ugh!
self.adaptor.set_app(subscribe)
clients.append(self)



class WSGISupportHandler(WSGIRequestHandler):
def setup(self):
self.rfile, self.wfile = self.request


seekして巻戻すのを忘れていたせいでかなり時間を無駄にした。requestの完了をなんらかの方法で検出して、検出できたときにseek(0)してhandlerを呼ぶようにすべきだ(dataReceivedで全部もらえる保証がない為)が、とりあえずは放置。

def handle(self):
self.rfile.seek(0) #UGH! UGH! UGH!
WSGIRequestHandler.handle(self)



#/usr/bin/python

import StringIO

from twisted.internet import reactor, protocol

from SocketServer import BaseServer, TCPServer
from wsgiref.simple_server import WSGIRequestHandler, WSGIServer, demo_app
import cgi

import simplejson

clients = []

class WSGIAdaptor(WSGIServer):
application = None

def __init__(self, server_address, RequestHandlerClass):
print 'WSGIAdaptor:__init__'
BaseServer.__init__(self, server_address, RequestHandlerClass)
#self.socket = socket.socket(self.address_family,
# self.socket_type)
self.server_bind()
#self.server_activate()

def server_bind(self):
"""Override server_bind to store the server name."""
print 'WSGIAdaptor::server_bind'
#TCPServer.server_bind(self)
#HTTPServer.server_bind(self)
self.server_name = 'localhost' # fake
self.server_port = '9999' # fake
self.setup_environ()
self.base_environ['server'] = self

def get_message(self):
return self.message

def set_message(self, message):
self.message = message


class WSGISupportHandler(WSGIRequestHandler):
def setup(self):
self.rfile, self.wfile = self.request

def finish(self):
pass

def handle(self):
self.rfile.seek(0) #UGH! UGH! UGH!
WSGIRequestHandler.handle(self)

def parse_request(self):
print 'raw_requestline', self.raw_requestline
ok = WSGIRequestHandler.parse_request(self)
print "WSGIRequestHandler:parse_request", ok
if not ok:
print 'rfile'
print self.rfile.getvalue()
print 'wfile'
print self.wfile.getvalue()

return ok


def subscribe(environ, start_response):
stdout = StringIO.StringIO()
q = cgi.parse_qs(environ['QUERY_STRING'])
msg = environ['server'].get_message()
value = {'message':msg, 'who':'hogeo'}
j = '%s(%s);'%(q['callback'][0], simplejson.dumps(value))
print >> stdout, j
start_response("200 OK", [('Content-Type','text/javascript')])
return [stdout.getvalue()]


class Subscription(protocol.Protocol):
def connectionLost(self, reason):
pass

def connectionMade(self):
print 'Subscription::connectionMade'
self.rfile = StringIO.StringIO()#'rb', self.rbufsize)
self.wfile = StringIO.StringIO()#'wb', self.wbufsize)
self.adaptor = WSGIAdaptor(('localhost', 3165), WSGISupportHandler) #ugh!
self.adaptor.set_app(subscribe)
clients.append(self)

def dataReceived(self, data):
self.rfile.write(data)

def sendMessge(self, msg):
adaptor = self.adaptor
adaptor.set_message(msg)
peer = self.transport.getPeer()
adaptor.finish_request((self.rfile, self.wfile), peer)
print '=== Subscriver (request) ===='
print self.rfile.getvalue()
print
print '=== Subscriver (response) ===='
print self.wfile.getvalue()
print
self.transport.write(self.wfile.getvalue())

class Subscriver(protocol.ServerFactory):
def buildProtocol(self, addr):
return Subscription()



def publish(environ, start_response):
stdout = StringIO.StringIO()
print 'publish'
q = cgi.parse_qs(environ['QUERY_STRING'])
msg = q['message'][0]
print msg
environ['server'].set_message(msg)
value = {'status': True}
j = '%s(%s);'%(q['callback'][0], simplejson.dumps(value))
print >> stdout, j
start_response("200 OK", [('Content-Type','text/javascript')])
return [stdout.getvalue()]


class Publication(protocol.Protocol):
def connectionMade(self):
print 'Publication::connectionMade'
self.rfile = StringIO.StringIO()#'rb', self.rbufsize)
self.wfile = StringIO.StringIO()#'wb', self.wbufsize)
self.adaptor = WSGIAdaptor(('localhost', 3124), WSGISupportHandler) #ugh!
self.adaptor.set_app(publish)

def connectionLost(self, reason):
pass

def dataReceived(self, data):
self.rfile.write(data) #ugh!
print '=== Publisher ===='
print data
print
peer = self.transport.getPeer()
self.adaptor.finish_request((self.rfile, self.wfile), peer)
msg = self.adaptor.get_message()
for c in clients:
c.sendMessge(msg)
self.transport.write(self.wfile.getvalue())


class Publisher(protocol.ServerFactory):
def buildProtocol(self, addr):
return Publication()


reactor.listenTCP(3165, Subscriver())
reactor.listenTCP(3124, Publisher())
reactor.run()

2009年12月23日水曜日

つまる。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
どうしたんだろう。ProtocolのsubclassのconnectionMadeがdataReceivedのあとに呼ばれている?!なにが間違っているのやら。


Traceback (most recent call last):
File "/usr/lib64/python2.4/site-packages/twisted/python/log.py", line 51, in callWithLogger
return callWithContext({"system": lp}, func, *args, **kw)
File "/usr/lib64/python2.4/site-packages/twisted/python/log.py", line 36, in callWithContext
return context.call({ILogContext: newCtx}, func, *args, **kw)
File "/usr/lib64/python2.4/site-packages/twisted/python/context.py", line 59, in callWithContext
return self.currentContext().callWithContext(ctx, func, *args, **kw)
File "/usr/lib64/python2.4/site-packages/twisted/python/context.py", line 37, in callWithContext
return func(*args,**kw)
--- ---
File "/usr/lib64/python2.4/site-packages/twisted/internet/selectreactor.py", line 146, in _doReadOrWrite
why = getattr(selectable, method)()
File "/usr/lib64/python2.4/site-packages/twisted/internet/tcp.py", line 362, in doRead
return self.protocol.dataReceived(data)
File "cometd.py", line 114, in dataReceived
self.rfile.write(data) #ugh!
exceptions.AttributeError: Publication instance has no attribute 'rfile'
Subscription::connectionMade


あ・・・methodを上書きして消していた・・・orz

2009年12月22日火曜日

Twistedでcometする一歩目

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Python:Twistedの短いソースコードのコード(下)を起点にできる限り単純なcometなコードを書こう。

from twisted.internet import reactor, protocol

class MyProtocol(protocol.Protocol):
def connectionLost(self, reason):
pass

def dataReceived(self, data):
print data
self.transport.write('test message\n')
self.transport.loseConnection()

class TCPServer(protocol.ServerFactory):
def buildProtocol(self, addr):
return MyProtocol()

reactor.listenTCP(12345, TCPServer())
reactor.run()


まずは動きを理解するためにそのまま実行。

サーバ側

[nori@shinano]~/Desktop/work/jsboard/comet/sample% python twisted-server.py
hello



クライアント側

[nori@shinano]~/Desktop/work/jsboard/comet/sample% telnet 127.0.0.1 3165
Trying 127.0.0.1...
Connected to shinano.tonic-water.com (127.0.0.1).
Escape character is '^]'.
hello
test message
Connection closed by foreign host.

self.transport.loseConnectionのおかげで切れてしまいます。そこでこれをコメントアウトし、つなぎっぱにしてふたつtelnetを立ち上げてつなげると、つながります。つまりちゃんと多重化されているんです。

これでは何もできないのでtransportやprotocolのメンバを調べます。本家のapi reference

2 Twisted解剖図鑑 を見ながら進めていきます。reactorが差し替えられる云々の話が出てきますが、ここが話が速い。今回はUIがない、デーモンを作るのであまり気にしなくていい。また、ここの例ではlineReceivedのなかでsendResponseしているが、このままではCometにならない。sendResponseに相当するメソッドを、別のタイミング(chat serverならbroadcastするタイミング)で呼んでやらないといけない。HTTP GETをなんらかの形で覚えておいて外部から指定されたタイミングで返す。そのために使えるAPIを探すことになる。

とりあえずはchat serverっぽい動きをするようにしてみた。connectionが切れたときにunregisterされていないのはご愛嬌。

#/usr/bin/python

# from
# http://omake.accense.com/wiki/Twisted%E3%81%AE%E7%9F%AD%E3%81%84%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89%E9%9B%86


from twisted.internet import reactor, protocol

host = 'localhost'
port = 3165


clients = []
class MyProtocol(protocol.Protocol):
def connectionLost(self, reason):
pass

def connectionMade(self):
clients.append(self)

def dataReceived(self, data):
print data
for c in clients:
c.sendMessge(data)

def sendMessge(self, msg):
self.transport.write(msg)

class TCPServer(protocol.ServerFactory):
def buildProtocol(self, addr):
return MyProtocol()

reactor.listenTCP(port, TCPServer())
reactor.run()


reactorに何か追加すればよいのだろう。listenTCPの代わりになにかメソッドを追加するか、もしくはほかのサーバと連携するための口としてTCPを使ってしまい、もうひとつProtocolを書く。その中でclientsをなめてsendMessageすればよい。

とりあえず、TCP version

#/usr/bin/python

from twisted.internet import reactor, protocol


clients = []

class Subscription(protocol.Protocol):
def connectionLost(self, reason):
pass

def connectionMade(self):
clients.append(self)

def dataReceived(self, data):
pass

def sendMessge(self, msg):
self.transport.write(msg)

class Subscriver(protocol.ServerFactory):
def buildProtocol(self, addr):
return Subscription()


class Publication(protocol.Protocol):
def connectionLost(self, reason):
pass

def connectionMade(self):
pass

def dataReceived(self, data):
for c in clients:
c.sendMessge(data)

def sendMessge(self, msg):
pass

class Publisher(protocol.ServerFactory):
def buildProtocol(self, addr):
return Publication()


reactor.listenTCP(3165, Subscriver())
reactor.listenTCP(3124, Publisher())
reactor.run()

2009年12月19日土曜日

xorg.conf

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
with ati-driver-installer-9-10-x86.x86_64.run

ati-driver-installer-9-12-x86.x86_64.runはなぜかうまく動かない。


Section "ServerLayout"
Identifier "Multihead layout"
Screen 0 "Screen0" 1920 0
Screen 1 "Screen1" 0 0
InputDevice "Keyboard0" "CoreKeyboard"
EndSection

Section "Files"
EndSection

Section "Module"
Load "GLcore"
Load "glx"
Load "dri"
Load "vbe"
Load "dbe"
EndSection

Section "ServerFlags"
Option "AIGLX" "on"
Option "Xinerama" "on"
Option "Clone" "off"
EndSection

Section "InputDevice"
Identifier "Keyboard0"
Driver "kbd"
Option "XkbModel" "pc101"
Option "XkbLayout" "us"
Option "XkbVariant" "intl"
EndSection

Section "Monitor"

### Comment all HorizSync and VertSync values to use DDC:
Identifier "Monitor0"
VendorName "BenQ"
ModelName "G2220HD"
DisplaySize 480 270
HorizSync 31.5 - 37.9
VertRefresh 50.0 - 70.0
Option "VendorName" "ATI Proprietary Driver"
Option "XaaNoOffscreenPixmaps" "true"
Option "DPMS" "true"
EndSection

Section "Monitor"
Identifier "Monitor1"
VendorName "BenQ"
ModelName "G2220HD"
DisplaySize 480 270
HorizSync 31.5 - 37.9
VertRefresh 50.0 - 70.0
Option "VendorName" "ATI Proprietary Driver"
Option "XaaNoOffscreenPixmaps" "true"
Option "DPMS" "true"
EndSection

Section "Device"
Identifier "RADEON-HD-4850-port0"
Driver "fglrx"
BoardName "ATI Technologies Inc RV770 [Radeon HD 4850]"
BusID "PCI:2:0:0"
Screen 0
EndSection

Section "Device"
Identifier "RADEON-HD-4850-port1"
Driver "fglrx"
BoardName "ATI Technologies Inc RV770 [Radeon HD 4850]"
BusID "PCI:2:0:0"
Screen 1
EndSection

Section "Screen"
Identifier "Screen0"
Device "RADEON-HD-4850-port0"
Monitor "Monitor0"
DefaultDepth 24
SubSection "Display"
Viewport 1920 0
Depth 24
Modes "1920x1080"
EndSubSection
EndSection

Section "Screen"
Identifier "Screen1"
Device "RADEON-HD-4850-port1"
Monitor "Monitor1"
DefaultDepth 24
SubSection "Display"
Viewport 0 0
Depth 24
Modes "1920x1080"
EndSubSection
EndSection

Section "DRI"
Mode 0666
EndSection

Section "Extensions"
Option "RENDER" "ENABLE"
Option "Composite" "Disable"
#http://builder.japan.zdnet.com/news/story/0,3800079086,20365215,00.htm
EndSection