0

AndroidでHTTP POST

targetはAndroid2.3.4、API 10向けにビルドして試した。

参考:


<uses-permission android:name="android.permission.INTERNET" />

import java.io.*;
import java.util.*;
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

HttpClient client = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://localhost:8080");
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("message", "ほむ"));
try{
httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
HttpResponse res = client.execute(httppost);
ByteArrayOutputStream os = new ByteArrayOutputStream();
res.getEntity().writeTo(os);
Log.v("result", os.toString());
Log.v("status", res.getStatusLine().getStatusCode());
}
catch(Exception e){
e.printStackTrace();
}


リクエストができているかの確認は、EM::HttpServerで見た
gem install eventmachine_httpserver
#!/usr/bin/env ruby
require 'rubygems'
require 'eventmachine'
require 'evma_httpserver'

class Handler < EM::Connection
include EM::HttpServer

def process_http_request
res = EM::DelegatedHttpResponse.new(self)
puts "request_method : #{@http_request_method}"
puts "path_info : #{@http_path_info}"
puts "query_str : #{@http_query_string}"
puts "post_content : #{@http_post_content}"
res.status = 200
res.content = "こんにちは"
res.send_response
end
end

EM::run do
EM::start_server("0.0.0.0", 8080, Handler)
puts "http server start, prot 8080"
end

0

serial-http-gateway作った

シリアルポートをhttpで使えるツールを作った。
ブラウザでhttp://localhost:8783を開くとデータが読める。POSTでデータを送ると書き込める。

webブラウザでロボットを操作する部分がある、OB降臨システムというのを作っているのでその部品として作った。

githubに全部置いた。



■インストールと起動
git clone git://github.com/shokai/serial-http-gateway.git
cd serial-http-gateway
gem install serialport eventmachine eventmachine_httpserver json ArgsParser


Arduino等をMacに接続すると/dev/tty.usb〜〜という名前になる。引数に渡して起動する。
./serial-http-gateway --help
./serial-http-gateway /dev/tty.usbserial-A7006Rqn
port 8783で起動する。引数-portで変更できる。



gemが全て入っていれば実行ファイル単体で動くので、適当なパスが通っている場所にコピーして置くと便利。
sudo cp serial-http-gateway /usr/local/sbin/



■使う
HTTP POSTでシリアルポートに書き込める。
% curl -d 'testtest' 'http://localhost:8783'


HTTP-GETでシリアルポートからのデータが読める。
最近100件の受信データが保存してあって配列で返ってくる。timeに時間が入っている。
curl 'http://localhost:8783'


時間はミリ秒でunixtimeなので、1000で割れば普通のunixtimeになる
[{"data":287,"time":1296767483756},{"data":288,"time":1296767483253},{"data":291,"time":1296767482751},{"data":293,"time":1296767482246},{"data":292,"time":1296767481743},{"data":293,"time":1296767481238},{"data":294,"time":1296767480736},{"data":299,"time":1296767480233},{"data":303,"time":1296767479729},{"data":305,"time":1296767479226},{"data":307,"time":1296767478721},{"data":312,"time":1296767478219},{"data":321,"time":1296767477714},{"data":332,"time":1296767477211},{"data":344,"time":1296767476709},{"data":359,"time":1296767476204},{"data":"\u0000390","time":1296767475701}]


eventmachine_httpserver便利だなー

0

EventMachine::WebSocketでチャットを作る

Ruby & WebSockets: TCP for the Browser – igvita.comを参考にした。

websocketなのでchromeかsafariかiPhoneで動くけど、FirefoxとIEでは動かない。



■サーバー

サーバーはrubyのEventMachine::WebSocketを使った。
まずgemでインストールしておいて
gem install em-websocket


EM::runの中でEM::WebSocket.startするだけ。
1プロセスに複数クライアントつなぎっぱなしにして、全クライアントにまとめてメッセージを送るためにEM::Channelを使った。EM::WebSocketのexamplesの中に入ってたtwitterのstreamを流すサーバーでも使ってた。
EM::Channelにpushで何か入れると、あらかじめsubscribeで登録しておいたブロック全てにそれが渡されて実行される。onopenした時にsubscribeしておくとクライアントを管理できる。
server.rb
require 'rubygems'
require 'em-websocket'

MAX_LOG = 100

EM::run do

puts 'server start'
@channel = EM::Channel.new
@logs = Array.new
@channel.subscribe{|mes|
@logs.push mes
@logs.shift if @logs.size > MAX_LOG
}

EM::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |ws|
ws.onopen{
sid = @channel.subscribe{|mes|
ws.send(mes)
}
puts "<#{sid}> connected!!"
@logs.each{|mes|
ws.send(mes)
}
@channel.push("hello <#{sid}>")

# channel登録時のidを使うためにonopen内で他のイベント登録を済ませる
ws.onmessage{|mes|
puts "<#{sid}> #{mes}"
@channel.push("<#{sid}> #{mes}")
}

ws.onclose{
puts "<#{sid}> disconnected"
@channel.unsubscribe(sid)
@channel.push("<#{sid}> disconnected")
}
}
end

EM::defer do
loop do
puts Time.now.to_s
@channel.push Time.now.to_s
sleep 60*60*3 # 3時間ごと
end
end
end
websocketのつなぎっぱなし感を試したかったので、EM::deferも回しておいて3時間ごとにEM::Channelに時刻をpushしてみた。全チャットクライアントに時刻が表示される。


後から接続してきたクライアントのために、メモリ上に配列で100件ログを取っておくことにした。
新しいクライアントが来たらログの中身をまとめてsendする。プロセス自体はforkしないで全クライアントまとめて接続させてるからDBが必要ない。


サーバーは
ruby server/server.rb
でport8080で起動する。



■クライアント

次にクライアント。
ごくふつうのjQueryを読み込んだhtmlを書いておいて、
new WebSocketでサーバーに接続してonmessageとonopenとoncloseイベントを登録して、WebSocket.send関数で送信するだけ。
サーバーが再起動した時にそなえてoncloseしたらsetIntervalで定期的に接続しなおすようにすると良さげ。
var ws = new WebSocket("ws://localhost:8080");
ws.onmessage = function(e){
trace(e.data);
};
ws.onclose = function(){
log("ws closed");
};
ws.onopen = function(){
log('connected!!');
};

$(function(){
$('input#post').click(function(){
var name = $('input#name').val();
var mes = $('input#message').val();
ws.send(name+" : "+mes);
$('input#message').val("");
});
});

function log(message){
trace("[log] "+message);
};

function trace(message){
var mes_div = $('<div />').html(message);
$('div#chat').prepend(mes_div);
};



■動作環境

rubyとem-websocketがインストールされてればどこでも動かせる。

websocketは、httpの80番portと別のportで起動しないとならんのでさくらのVPSにubuntu10.04入れたサーバーで動かしてる。
server.rbのプロセスが死んでも復活するようにdaemontools使った。

0

serial-socket-gateway

よくある シリアル通信 <=> TCP Socket する物を作った。
しばらく使ってて特に問題なかったので、使い方とか書いておく。
Mac OSX LeopardとUbuntu 9.04,と10.04とWindows XPで動作した。

普通のsocketなので大抵のプログラム言語から使えるし、少なくとも10個ぐらいのプログラムを下にぶらさげてマイコンと通信させられる。
最近はコレでsocket化したシリアルデバイスにJRubyで作ったGUIをつないで色々やってる。


■ソースコード
githubにある
http://github.com/shokai/serial-socket-gateway


■インストール

git clone git://github.com/shokai/serial-socket-gateway.git

中のserial-socket-gatewayがそれ。Rubyで書かれている。
単体のファイルで動くので、そのまま /usr/local/bin/ とか適当なパスの通っているディレクトリに cp すればいいと思います


■必要なgemのインストール
ruby-serialportとeventmachineを使っているのでそれぞれインストールする。

gem install eventmachine ArgsParser


ruby-serialportは0.7が動かなかったので、俺は0.6を使っている。MacやUbuntuはソースからビルドしてインストーrうする
wget http://rubyforge.org/frs/download.php/72/ruby-serialport-0.6.tar.gz
tar -zxvf ruby-serialport-0.6.tar.gz
cd ruby-serialport-0.6
ruby extconf.rb
make
sudo make install


Windowsは、http://rubyforge.org/tracker/download.php/61/321/9924/1800/ruby-serialport-0.6.0-mswin32-gem.zipからwindows用のバイナリ(gem)を持ってきて、解凍してインストール。
gem install serialport-0.6.0-mswin32.gem


■起動
Arduino等をMacに接続すると /dev/ の下に、tty.usbなんとかというデバイスができているはず。
serial-socket-gateway /dev/tty.usbserial-A7006Rqn
デバイス名を引数に渡して起動。


接続してみる。telnetでok
telnet localhost 8782
デフォルトで8782番portで起動している。port番号はソースの上の方をいじれば変えられる。


■通信の仕様
改行(¥n)区切りで、1行毎に通信している。改行が来るまで送受信しない。


■サンプルプログラム Arduinoから送信
こんなかんじでADコンバータで電圧測って、Serial.println()を使って末尾に改行を付けて9600bpsパリティなしストップビット1で送信。
int ad_pin = 0;

void setup(){
Serial.begin(9600);
}

void loop(){
int ad = analogRead(ad_pin);
Serial.println(ad);
}


■サンプルプログラム Rubyで受信
間にserial-socket-gatewayを挟んでシリアルデバイスと通信する。

require 'socket'
s = TCPSocket.open("192.168.1.100", 8782)

loop do
res = s.gets
if res.to_s.size > 0
puts res
end
end
serial-socket-gatewayはclientとの接続が切れていないか確認する為に、たまに空文字列を送ってくる。
受信した文字列の長さを見て無視してください

送信は
s.puts "abc abc"
rubyのputsは末尾に改行(¥n)が付く。



■サンプルプログラム Rubyで送受信を同時に
標準入力をserial-socket-gatewayにそのまま送る。こういうのeventmachineでやると楽だね
#!/usr/bin/env ruby
require 'rubygems'
require 'socket'
require 'eventmachine'

HOST = 'localhost'
PORT = 8782

begin
s = TCPSocket.open(HOST, PORT)
rescue => e
STDERR.puts e
exit 1
end

EventMachine::run do
EventMachine::defer do
loop do
res = s.gets
exit unless res
if res.to_s.size > 0
puts res
end
end
end

EventMachine::defer do
loop do
s.puts gets
end
end
end


eventmachineが便利!!

0

skype-socket-gatewayを作った

Skypeのグループチャットにbotを放ったり色々したかったので作った。

Skype APIはSkypeが起動しているマシンの中でSkypeのプログラムと自分のアプリを通信させて使う物で、developer.skype.comにAPIのドキュメントがある。電話をかけたりチャットの中身を取得したり発言したりできる。

Skypeが起動しているマシンでskype socket gatewayを起動しておくと、他のマシンからもtelnetでSkype APIが使える。また、全チャット(含むグループチャット)のデータがJSONでどんどん流れてくるようになっている。

うちではWindows XPのネットブックでbotアカウントでSkypeを起動してあった。Skype chatはP2Pなのでログをそれぞれが持っていて、後で接続してきた人は先に居た人から取得する様になっている。だから、ただ何もしなくてもずっとオンラインの人がいるだけでログの流れが良くなる。
で、そのbotアカウントを真なるbotにするために、Windows用のActiveScriptRuby 1.8.7でskype socket gatewayを動かしてそこに別のLinuxのサーバーから複数のbotが接続して色々処理をしている。

Linux上で動くSkype用のbotを作る方法 – muddy brown thangにXvfbを使ってlinuxで仮想フレームバッファを作ってSkype APIを使う方法が解説されてるけど、なんかLinuxが難しくて挫折したのでwindowsでも動くsocket gatewayを作った。


■ソースコード
githubに全部置いておいた
shokai's skype-socket-gateway at master – GitHub


■インストール

git clone git://github.com/shokai/skype-socket-gateway.git

必要なgemもインストールする
gem install Ruby4Skype json eventmachine

Ruby4Skypeというのを使っているので、Windows、Mac、Linuxで動く。ただしRuby4Skypeが64bit MacOSXでは動かない。Linuxも、Ruby4Skypeのlib/skype/os/linux.rbを修正してinitialize methodのsuperにapp_nameを渡すように修正しないと動かない。

それとWindowsでRuby4Skypeを動かすには色々DLLを入れないとダメ → WinXPでgem install Ruby4Skypeするメモ – yuisekiのいまさらruby厨日記 – Rubyist


■起動する
先にSkypeを起動しておく。

ruby skype-gateway.rb
でport 20000で起動する。


■使う
telnetで接続する。
telnet 192.168.1.37 20000

そしてそのまま
CALL shokaishokai
MESSAGE shokaishokai hello work!!
とか書く。Skype APIがそのまま実行できる。
レスポンスはJSONで来る。

あと、接続しているだけで全てのchatの発言が流れてくる。


■Windowsのスタートアップに登録する
run_skype_gateway.bat というバッチファイルを作る
"C:\Program Files\ruby-1.8\bin\ruby.exe" "C:\Document and Settings/sho/src/ruby/skype-socket-gateway/skype-gateway.rb"
中身はこの1行でいい。これをスタートメニューのスタートアップに入れておけば自動起動できる。


■Skype botを作る
RubyだとTCPSocketクラスを使うと簡単に作れる。

キーワードに反応する単純なbotの例。誰かが「ざんまい」と書くと、即座に「ざんまい行きたい!」と返す。
samples/zanmai_bot.rb at master from shokai's skype-socket-gateway – GitHub
require 'rubygems'
require 'socket'
require 'json'
require 'eventmachine'
$KCODE = 'u'

HOST = "192.168.1.37"
PORT = 20000

begin
s = TCPSocket.open(HOST, PORT)
s.puts "MESSAGE shokaishokai ざんまいbot start"
rescue => e
STDERR.puts e
exit 1
end

EventMachine::run do

EventMachine::defer do
loop do
res = s.gets
exit unless res
res = JSON.parse(res) rescue next
p res
if res['type'] == 'chat_message'
if res['body'] =~ /ざんまい/ # キーワードに反応
s.puts "CHATMESSAGE #{res['chat']} ざんまい行きたい!"
elsif res['body'] =~ /かず(すけ|助)/
s.puts "CHATMESSAGE #{res['chat']} かずにゃんぺろぺろ"
end
end
end
end

EventMachine::defer do
loop do
mes = gets
s.puts mes if mes
end
end

end


これにもう少し語彙を追加すると、だいたい俺の人格と機能を8割方再現したbotが作れる。
shokai_bot.rb at master from shokai's skype-bots – GitHub
6a8dae3e0028f2f870f194cdbec2af75.png


あと、全chatをmongo dbに入れて検索したりできるbotなんかもいる。超便利。
tools/skype_chat_store_mongod.rb at master from shokai's skype-socket-gateway – GitHub
fecba229c251c20a4543e21bd1395929.png


はてなランドに自分のskypeの発言を送るなんて事もできる
hatenaland_proxy.rb at master from shokai's skype-bots – GitHub


■EventMachine
EventMachineというサイバーパンクな名前のライブラリがRubyにあって、これを使うと並行処理が簡単に書けた。
skype socket gatewayでは
  • 標準入力をSkype APIとして実行する
  • 接続してきたTCP Socketクライアントそれぞれからの入力をSkype APIとして実行し、結果をそれぞれに返す
  • 全ソケットクライアントに全chat logを送る
  • クライアントが接続されているかたまに確認する
という処理をやっていて、これらを適当にEventMachine::defer doの中でloopで書いてやればうまく動いた。

不思議だったのは、新しくacceptしたsocket clientオブジェクトそれぞれを個別に処理するためにEventMachine::deferの中でclient毎にそれぞれEventMachine::deferを作ってみたら、ちゃんとスコープが別になってくれた。
  EventMachine::defer do
loop do
s = sock.accept
clients << s
puts "--- new client : #{clients.size}"

# socket -> invoke Skype API
EventMachine::defer do
loop do
cmd = s.gets
next if cmd == nil or cmd.to_s.size < 1
puts "recv => #{cmd}"
begin
p res = {
:type => 'api_response',
:body => skype.invoke(cmd).to_s
}
rescue => e
res = {
:type => 'error',
:body => 'skype api invoke error'
}
STDERR.puts e
end
begin
s.puts "\n"+res.to_json
rescue => e
STDERR.puts e
c.close
break
end
end
end
end
end


それぞれのdeferの中でAPI呼び出しと結果を返す処理が動いている。これをthreadでやる時
Thread::start(s) do |s|
loop do
res = s.gets
# 何か処理
s.puts response.to_json
sleep 0.01
end
end
みたいに引数に渡さなくても勝手にスコープ分けてくれてた。


でも、clientの配列とかchatメッセージのqueueを作って複数のEventMachine::deferからdeleteしたりpushしたりしていじっている部分では、defer間で共有されていた。不思議だ。
EventMachine::run do
# forward all skype chats -> socket
EventMachine::defer do
loop do
if chat_msgs.size > 0
msg = "\n"+chat_msgs.shift.to_json
clients.each{|c|
begin
c.puts msg
rescue => e
STDERR.puts e
end
}
end
end
end

# check clients connection
EventMachine::defer do
loop do
msg = ''
errors = Array.new
clients.each{|c|
begin
c.puts msg
rescue => e
STDERR.puts e
errors << c
end
}
errors.each{|c|
clients.delete(c)
c.close
}
sleep 15
end
end

よくわからないけどEventMachineが凄い。