<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>
2009年12月24日木曜日
client part
んで、前々回のエントリのクライアントサイド。submitでreturn falseして、それ以上のsubmit動作を抑制しなきゃいけないことを忘れていたのは公然の秘密。
chrome for linux
LSBのバージョンが3.2である必要があるといわれたが、CentOSでは3.1でしかないので無理矢理いれてみた。
ランチャのプロパティを見ると起動のpathが
になっているが、そんなものは無い。orz
[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)。
seekして巻戻すのを忘れていたせいでかなり時間を無駄にした。requestの完了をなんらかの方法で検出して、検出できたときにseek(0)してhandlerを呼ぶようにすべきだ(dataReceivedで全部もらえる保証がない為)が、とりあえずは放置。
グチャグチャなのだが、恥を忍んで公開。
コアとなるアイディアは、標準の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 '=== Subscriver (response) ===='
print self.wfile.getvalue()
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
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のあとに呼ばれている?!なにが間違っているのやら。
あ・・・methodを上書きして消していた・・・orz
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なコードを書こう。
まずは動きを理解するためにそのまま実行。
サーバ側
クライアント側
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されていないのはご愛嬌。
reactorに何か追加すればよいのだろう。listenTCPの代わりになにかメソッドを追加するか、もしくはほかのサーバと連携するための口としてTCPを使ってしまい、もうひとつProtocolを書く。その中でclientsをなめてsendMessageすればよい。
とりあえず、TCP version
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はなぜかうまく動かない。
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
登録:
投稿 (Atom)