*[Golang] いろいろ
Goのコンパイラが吐き出すコードを少し眺めてみる.もう少し最適化頑張って欲しいなぁ.gccgoとかだとまた変わるんだろうけどGCが無いのが難点.
GoでCGIを書く(http/cgi)
いつのまにか,http/cgiが出来ていたので試しに使ってみる.ただCGIだとオーバーヘッド大きそうなので,http.ListenAndServeを使ってサーブレットとして動かすのが良さそう.
package main
import "fmt"
import "http/cgi"
func main() {
var _,err = cgi.Request()
if err != nil {
fmt.Println("Please run as a CGI");
return
}
q := req.FormValue("q")
fmt.Printf("Content-Type: text/plain\n\n")
fmt.Printf("hello, world! %s \n",q)
}
最初はこう書いてたのだけど.
package main
import "fmt"
import "http"
import "http/cgi"
func main() {
cgi.Serve(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
q := req.FormValue("q")
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w,"hello, world! q= %v \n",q)
}))
}
ResponseWriterとRequestを受ける関数にしておくと,http.ListenAndServeで動かしたときとか,GAEでもそのまま使えるので,こっち使うべきですね.
JSON
標準で用意されてるjsonパッケージ使って簡単に扱えます.
が……
type User struct {
id int;
name string;
}
func (u *User) Name() string {return u.name}
func (u *User) Id() string {return u.id}
という良くある構造体をMarshal()してみたけど,空のオブジェクトとして扱われてしまった.
json.Marshalの説明をよく読むと,「Only exported fields will be encoded.」って.
もしかして,構造体のフィールドを外部に公開しないといけないんですか.
type User struct {
Id_ int "id";
Name_ string "name";
}
func (u *User) Name() string {return u.Name_}
func (u *User) Id() string {return u.Id_}
-
id,nameを Id,Nameにしてみた
-
既にあるゲッターと名前が被ったので, Id_, Name_に
-
Jsonとして出力される名前をタグで指定
絶対に何かが違うと思うというか,間違っている気がして仕方がない.
JSONに使うオブジェクトは,ゲッターやセッターは用意せずにフィールドを直接公開しておくのが良いのか.
後でもう少し調べてみよう.
名前を大文字にする以外に,外部に公開する手段を用意して欲しいなぁ.
MySQL
GoでMySQL使うためのパッケージ色々あるけど,未完成っぽいものが多いなぁ.
https://github.com/Philio/GoMySQL
これが一番完成度高そう.ざっとソース読んだ感じ,必要な物がそろってる気がする.
MySQLとか手元に環境が無いので会社で試そう.
XXX declared and not used.
goのプログラムのデバッグ中に一時的に一部のコード消したりすると,「XXX declared and not used.」というエラーが発生して面倒なので,せめてデバッグ中だけでも使われない変数許容して欲しかったのだけどできないっぽい…….Goのソースを見たら,疑わしきは罰するという強い意志を感じたのであきらめる.
package main
import "fmt"
func main() {
var p, e = 3.141593, 2.718282
fmt.Printf("hello, world %v\n",e)
}
このコードからエラーを取り除くために
と書いてみたら,左辺に変数が無いといわれてしまったので,
と書いてみたらコンパイルが通った.
しかし,もう一つ変数に触ってるのが何か嫌だったので,
これでも許してくれた.
他には,
もありかと思ったけど,今のGoのコンパイラだと余分なJMP命令がコード中に混入するのでよろしくない.
歯医者行って寝る.
最近はPerlからRubyに移りつつあるのだけど,Rubyだと速度面で不満なのでGoを試している.コンパイル速度も速いのでインタプリタ感覚で使っても大丈夫そう.
さくらVPS上で単純なHTMLを生成するCGIを書いてレスポンスにかかる時間を計った感じだと
-
Go: 10ms
-
Perl: 32ms
-
Ruby: 98ms
という結果に…….静的なファイルでも9msかかってるので,Goはかなり優秀.
というか,さくらのVPS速いな.ネットワーク的に近いのもあるのだろうけど,空いてるときなら80Mbps近く出てレイテンシも数ミリ秒なので,LAN内のマシンと区別せず扱える.
少しGoに触ってみたりする.Hello,world!っぽいサンプル動かしたことはあったのですが,あんまりまともに書いてなかったので.
stringはいいとして,mapも組み込み型なのか.ライブラリとして実装されてたほうが拡張が効くと思うのだけど,演算子オーバーロード無いし組み込み型の方が使いやすくて良いな.
クラスが無いのも良いかと思ってたのだけど,いつの間にかクラス指向に洗脳されていたようで,実際に書いてみると不便に感じる.多くのプログラマは既にクラスベースのオブジェクトに慣れてしまってるのでクラス無い言語だと後で面倒くさそうだなぁ.JavaScripとか見ててもクラスがあれば悲惨なコードがもう少し減った気がするし.
大したことでは無いのだけど,大文字で始まるメソッドが外部に公開されるというのが,イマイチだなぁ.メソッド名を日本語で書いたりしないから良いのかもしれないけれど.
新しい言語触るたびにまずコンパイラを書きたくなるのだけど,なんでだろう.コード書きながら,結局どういう命令列がCPUで実行されるのか,ということをつい考えてしまうからかも.
家出る前にX121e届いた.届いたが,時間が無いので開封もせずに会社に.箱ごと持っていこうかと思ったけど,今週は真面目に仕事するべきな気がしてるので,家においてくる.帰ってから遊ぶ.
*X121e
見た目がシンプルで,薄いのはとても良い.重さも許容範囲内.
ACアダプタは,X200と同じく65W 20Vのやつなので共有できる.
タッチパッド邪魔だなと思ったのだけど,トラックポイントで操作中は触っても誤動作しないし,心配すること無かった.
キーボードがThinkPadぽくないやつなので,ちょっと使いにくいけど慣れるしかないな.
X200と比べて良かったところ
-
安い
-
薄い
-
LCDがLEDバックライト
-
HDMI出力できる
微妙なところ
-
キーボード照らすLEDが無い……暗いところで使うときは地味に便利だったのに
-
ThinkPadロゴのiの字のドットが光るの邪魔な気がする
-
キー配置が普通のThinkPadと全く違う
-
トラックポイントの赤いポッチのゴムが1種類しか付いてない
-
ディスプレイの解像度……まあ仕方ない
CPUのスペックについてはまだよく分からないけど,いつも使ってる2.26GHzのCore2 Duoと比べると少し遅く感じるかも.ただ,グラフィック周りが良くなったせいか,動画を倍速再生してるときの負荷は減った気がする.
今日火曜日だと思っていたが違った.
*[ruby] RubyでCGI書くと遅いのをどうにかしたい
perlで書けば確実に50ms以内にレスポンスを返せる場面で1秒以上かかる.プロセス生成やライブラリ読み込みが遅いようなので,Webrick使って1プロセス内で完結するようにしてみたけど,それでも600ms以上かかる.
600msもかかるとかなり重く感じるので,どうにかしないとなぁ.passengerかmongrelも試してみよう.
歯医者行ったり,髪切ったり,昼寝したり,洗濯したりして一日が終わった.
円高進んでるなぁ.そろそろpaypalのドルは円に換えておくべきだと思うけど,もうしばらく放置しておこう.
そろそろb-mobile simの期限切れるから,どうにかしないとな.もう少し高速な回線が欲しいけど契約が面倒くさい.とりあえずFairとかにして,だめそうならイオンのやつ契約するか.
*b-mobile Fair
というわけで,秋葉原のヨドバシまで行ってb-mobile Fair買ってきました.amazonで買うと少し安いですが,ポイント分考えると似たようなものなのと,すでにたまっているポイントを使ってしまいたいので.完全にポイント戦略にのせられている…….
b-mobile SIM U300を1年使ってみたのですが,FOMAなのに300kbpsしか出ないのが不満だったので,それは解消です.
あとは,4ヶ月もしくは1GBを9,800円というのが,安いのか高いのかですね.後からチャージするときは8350円なので,2ヶ月使えればかなりお得な気がします.1ヶ月250MBくらいしか使わないなら,U300より安くなるけどそれはさすがに無い気がするな.
まずは何も考えずに使ってみて,様子見てみる予定.
朝からお腹いたい.クーラーつけっぱなしで寝てるのが悪いのかも.
*ThinkPad X121e
ThinkPad X121eがとても安いので注文した.Core i3モデル.
今のX200と比べて特に軽くなるわけでも,スペックが上がるわけでもなさそうだけど,安いから多少乱暴に扱ってもよさそうだな.
メモ
-
noop データなし
-
c
-
me 接続完了?
-
ae アプリケーションから
-
m エラーとか?
-
b 不明
-
stop 不明
ボットを書く日.
眠い.新宿でしゃぶしゃぶ食べた.kstmの現役の後輩に会った.
明日は休日らしいことを知った.
*[google] Google Channel APIに接続する(メモ)
Channel APIは,Googleのサーバに接続したクライアント間でリアルタイムにメッセージをやり取りするためのAPIです.
色々見てると,Google TalkやGoogle+とかのGoogleのサービスでも同じようなものが普通に使われているっぽい.自分で使いたい場合は,Google App Engine上で使うためのAPIが用意されているので,大量のクライアント同士でリアルタイムな通信が実現できます.自前でサーバ用意すると,コネクション数が多くなった時に大変なのですが,勝手にスケールしてくれるのが素晴らしいですね.
APIの使い方は,App Engineのドキュメント見れば分かります.JavaやGoから使えるようですね.
ここからが本題.
Webアプリケーションの作り方はわかったのですが,やっぱりクライアント自作して,ボット作りたいですよね.定期的にURLにアクセスしてポーリングとか前時代的なやり方から脱却しましょう.
APIの使い方は説明見れば分かるのですが,実際のプロトコルが分かりません….
まあ,実際に使われてるJavaScriptを読めばクライアント実装方法は分かるはずです.……Googleのサービスで使われているjsファイル読んだことある人なら,JavaScript読むのは真っ先に避けると思いますが.
とりあえず,GAEの某アプリケーションのChannelAPIに接続してみる.
トークン
まず,APIにアクセスするためのトークンが必要です.自分で作ったサービスなら,適当にtoken取得できるAPI用意しておけばよいです.
2時間で無効になるので定期的に取らないとダメみたいです.
Googleのサービスではtoken無しでチャンネルにアクセスしてたりするので,クライアントよりもGAEのアプリケーションを認証するのが目的かも?.
clid,gsessionidの取得
URL https://talkgadget.google.com/talkgadget/d
実際には,[0~999].talkgadget.google.com のホストが使われてます.
パラメータ
-
token トークン
-
xpc 接続の情報をjsonで渡す(詳細は不明…) cn,ppu,lpu cnはランダムな10文字
xpc={cn:ランダムな10文字,tp:null,ppu:appspot側のxpc_blankのURL,lpu:talkgadget側のxpc_blankのURL}
xpcは無くても接続できるようです(?).あとで適当なURL指定して挙動を調べたい.
レスポンスはJavaScriptですが,WcsDataClientのインスタンスを作っているところを見ると,
WcsDataClient(url,???,clid,gsessionid,???,???,token)
という引数になってるのが見えるのでこれを抜き出します.
test
bindする前に,接続のテストが必要みたいです.
URL: https://talkgadget.google.com/talkgadget/dch/test
パラメータ(共通):
-
VER=8 ChannelAPIのバージョン?
-
clid=XXXXXXXXXXX
-
gsessionid=XXXXXXXXXXX
-
prop=data プロパティ名
-
token= トークン
-
zx=xxxxxxx ランダム?
-
t=1
1回目
"[]" 空のjson配列が帰ってくる.
2回目
"111112"みたいなレスポンスがきますが,最後の"2"を受信する前にコネクションを切る必要がある.
bind
メッセージを取得します.何かあったときにデータがくるので,別スレッドで受信しつつ,もう一つのスレッドでリクエストを送る感じに.
URL: https://talkgadget.google.com/talkgadget/dch/bind
パラメータ(常に送る):
-
VER=8 ChannelAPIのバージョン?
-
clid=XXXXXXXXXXX
-
gsessionid=XXXXXXXXXXX
-
prop='data' プロパティ名
-
token トークン
-
(追加分はこの間に入る…たんぶん順序は関係ない)
-
zx=xxxxxxx ランダムな文字列
-
t=1 不明
パラメータ(場合によって送る):
-
RID リクエストID もしくは 'rpc'
-
SID セッションID? 最初のbindで取得する
-
AID 最後に受信したメッセージの番号
-
CVER=1 何かのバージョン
POSTする場合
-
count reqの数
-
ofs=0
-
req0_m 内容
-
req0_c clid
-
req0__sc=c clientのc?
レスポンス
size\n
json\n
size\n
json\n
.
.
.
1レスポンスに複数のメッセージ含まれます.
sizeはjsonのバイト数.jsonの最初の要素はメッセージの番号.json文字列は微妙にinvalidなjsonなので加工する必要があるかも.
データの送受信
まず,bindのURLに,RIDとCVERを指定してリクエストします.特にコマンドは送らず,"count=0"をPOST.
[[0,["c","XXXXXXSIDXXXXXXX",,8]],[1,["b"]]]
こんな感じのデータになってます.0番のメッセージにSIDが入っています.今はSID以外は捨ててOK.
次に,SID,RID,CI,AID,TYPEを指定してGET.よく分かりませんが,RID=rpc,CI=0,AID=1,TYPE=xmlhttpです.AIDはさっき受信した最後のメッセージのIDが1なのでそれを.TYPEは無くても良いみたいですが,一応.
このまま待っていれば,定期的にnoopというデータが届きます.一つの1データを受信しても,レスポンスが終わるわけではないので,受信が完了しなくても,順次データを取り出していきます.
ただ接続しただけでは,noopしか送られてこないようなので,["connect-add-client"]を送ってみると,色々流れ始めます.
RIDの値をインクリメントして,以下のようなデータをPOSTします.
'count'=>1,
'ofs'=>0,
'req0_m'=>'["connect-add-client"]',
'req0_c'=>clid,
'req0__sc'=>'c',
チャンネル制御 connect-add-client以外は分からず.
-
connect-add-client チャンネルにクライアントを参加させる
メッセージ
[[0,["c","XXXXXSIDXXXXXXX",,8]],[1,["b"]]] SID受け取る
[[1,2,17]] connect-add-clientしたときに帰ってくる.意味は不明
[[15,["noop"]]] データ無し.15はメッセージの番号
[[16,["c"["XXXXclidXXXXX",["ae","hoge"]]]]] アプリケーションからのデータ ae=app engine??
4分弱でHTTPの接続は切れてしまうので,切れたら再接続.
歯医者.暫く放置してしまった治療を再開.5月に歯を折ってから2ヶ月以上たってるのか.
どこかに出かけようかと思ったのだけど,夜まで寝てしまった.
*[ruby] Rubyのnet/httpでGoogleにログイン
アプリケーションからログインする方法は
このあたり読めば分かる.
普段はgemのhttpclient使ってるのだけど,net/httpでやってみる.SSL証明書確認しないとか,Cookieの送信でホスト名しか見て無いとかは後で考える.
require 'uri'
require 'net/https'
Net::HTTP.version_1_2
class HTTPClient
def initialize
@cookies = {}
@timeout = 60
end
attr_accessor :timeout, :cookies
def formencode data
return data.map{|k,v| k+'='+URI.encode(v.to_s)}.join('&')
end
def pre_proc uri,headers = nil
headers = headers || {}
if @cookies[uri.host]
headers['Cookie'] = @cookies[uri.host].map{|k,v|k+'='+v}.join(';')
end
http = Net::HTTP.new(uri.host,uri.port)
http.read_timeout = @timeout
if uri.scheme == 'https'
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
return http,headers
end
def post_proc uri,response
if response['Set-Cookie']
if ! @cookies[uri.host]
@cookies[uri.host] = {}
end
response.get_fields('Set-Cookie').each{|str|
k,v = (str.split(';'))[0].split('=')
@cookies[uri.host][k] = v
}
end
return response
end
def get url,headers = nil,&block
uri = URI.parse(url)
http,headers = pre_proc(uri)
r = http.get(uri.request_uri,headers,&block)
return post_proc(uri,r)
end
def post url,body,headers = nil,&block
uri = URI.parse(url)
headers = {'Content-type' => 'application/x-www-form-urlencoded'}
http,headers = pre_proc(uri,headers)
if body.kind_of? Hash
body = formencode(body)
end
r = http.post(uri.request_uri,body,headers,&block)
return post_proc(uri,r)
end
end
account = {
'accountType' => 'HOSTED_OR_GOOGLE',
'Email' => 'example@gmail.com',
'Passwd' => 'hoge',
'service' => 'ah', # GMAIL:mail GAE:ah CAL:cl PLUS:oz
'source' => 'gaetest',
}
client = HTTPClient.new
r = client.post('https://www.google.com/accounts/ClientLogin',account)
if r.body =~ /Auth=([^\s]+)/
r = client.get('https://example.appspot.com/_ah/login?continue=/&auth=' + $1)
end
Cookieセットされるので後は普通にアクセスするだけ.やっぱ,net/httpでやるのは無いな....ただ,gemsすら入ってない環境がたまにあるので,いざというときのために貼り付けておく.
秋葉原で肉食った.おごって貰う焼肉は美味しいですね.
ヨドバシを徘徊してから会社に戻る.
洗濯をした.毛布をしまおうと思ったのだけど,クリーニングとか出すの面倒なので洗濯機に突っ込んで洗濯してみた.明らかにキャパシティをオーバーしてたけど,なんとか洗濯できた.
12時間くらい寝た.なんだか体のあちこちが痛い.
にゃあさんがやっていたのを真似てGoogle+のプロフィールにリダイレクトするように設定してみた. http://binzume.net/+
昨日知ったのだけど,秋葉原の液晶工房なくなったのか.最近はジャンクの液晶買ってきて遊ぶことも無いのだけど,ちょっと寂しいな.
ラジオ会館も無くなるし,また秋葉は変わっていくのか.
*メモリ買ってきた
秋葉原行ってX200用にメモリを買って来た.4GBで3000円位だった気がする.8GB欲しかったけど,どうせOSが32ビットで意味無いので我慢しておく.これだけメモリが安いと,もう32ビットOSは選択肢に挙がらなくなるなぁ.
X200のメモリ換えるの初めてだけど,それらしいネジを2本外すだけだった.1GBのメモリが2枚刺さっていたので両方抜いてSlot0の方に新しいのを挿す.念のため起動して正常に認識されてるのを確認した.
メモリ交換したときに埃が気になったので,結局分解.中を掃除したらCPU温度が少し下がったので満足.
*[Android] OpenGLのglVertexPointer
寝る前にshimobayashiに頼まれてたソース読む.テクスチャ張ってないポリゴンが描画されないという問題だったけど,glBindBufferしてて,glVertexPointerの最後の引数が意図せずにoffsetして解釈されているのが原因だった.
というか,これってCではポインタで渡してたから理解できるのだけど,javaなら型が違うのだから配列をオフセットとして解釈しようとするのは不自然だよぁ.なぜこんな仕様まで再現しなくていいのに.まあ,単にOpenGLのラッパーなのだと考えよう.
なんだか体がだるいし,とても眠い….
月曜日は休み明けで調子でないし,金曜日は疲れてるので,まともに仕事できるのは,火水木の三日間な気がしている.
人形町にチカラめしが出来て気になっていたのだけど,昼間は混んでて行けなかった.なので,仕事終わった後に行ってみる.
店員の動きが興味深かった.というか,とても酷かったという噂を聞いて気になっていたので,店員を観察しながら「レコードがロックされてる」「不整合が起きた」という話に花を咲かせていた.まぁ,開店したばかりで慣れていないのは仕方ないとしても,他の店舗も同じシステムなんだろうか….熟練した人にとっては効率よいのかもしれないので,既存店舗のやり方をそのままコピーした感じなのかなぁ.
帰って色々やる予定だったけど,何もせず寝てしまった.
会社のPCが新しくなったのだけど,メモリが8GBあると快適だな.次に自分で買うPCもメモリたくさん積もう.
暑い.
会社.
AndroidのAuthenticatorあたりを調べる.SDKのAbstractAccountAuthenticatorを見ると,説明が書いてありますね.意外と簡単にAccountManagerでアカウント管理できるようになりそう.
選択したり買い物行ったり.
昨日アップデートしたニコニコPlayer(仮)にいくつかバグあったので直したり.
秋葉原でeldeshと寿司食べる.
*ニコニコPlayer(仮)
最近放置してたニコニコPlayer(仮)をアップデート.0.1.15.
変更点
-
ニコニコチャンネル対応(仮)
-
Flashを使った再生時にコメントがスムーズに動かないのを改善
-
処理の見直しをしてアプリケーションの容量削減
-
エラー時に強制終了することがある問題を修正
-
細かい修正
ニコニコチャンネルのメニューが増えた以外は,特に大きな変更は無い気がする.機能追加はしたけど,アプリのサイズは小さくなってます.今後も,200KB以内に収まるようにしていきたい.
機能には全く関係ないけど,最初に表示されるメニュー画面を作り直しました.画面の幅に合わせてアイコンの列数を変えたかったので,今までLinearLayoutに入れていたのをGridViewを使いました.
AdMob
AdMobも殆どログインもせずに放置してたけど,最近またたくさん入ってるな.広告のインプレッション数は常に40万付近なので,収入の上下は完全に良い広告があるかどうかみたい.
色々疲れた
*Google+
最近,Google+はじめました.Facebookで挫折したので,こっちで頑張ります.
https://plus.google.com/103058473502536566874/about
↑アカウント持ってる人は適当にサークルに突っ込んでください.
twitterと違ってURLに含まれるIDが数字なので,ネット以外の場所で教えるのが面倒ですね.私はgmailアカウント公開してるし,IDをそのままURLにするオプションもあれば良いと思うのだけど.