0

NanoHTTPDでJavaアプリにwebサーバーを埋め込む

Javaで1ファイルで実装されたNanoHTTPDを触ってみた。
Apacheみたいなのが起動するんじゃなくて、自分のプロセスにwebサーバーの機能を埋め込むタイプ。

NanoHTTPD
https://github.com/elonen/nanohttpd


Androidで動かしている例も検索するとそれなりに出てくる。すんなり動くらしい。
Android上でWeb serverを動かしてみた – komamitsu.log
Androidアプリ開発に挑戦: Android で NanoHTTPD を使ってみる


とりあえずMacで試した。Androidではまだ試してない。
手元のファイルをHTTPで配信もできるし、リクエストのパスやメソッドやプロパティを読むことも出来る。
Androidのプログラムに埋め込めば、パソコンのwebブラウザからAndroidにアクセスして遠隔操作できて便利だと思う。


使い方

ドキュメントが無いけど実装がシンプルなのでgithubの本体のコードを見た。
new NanoHTTPD(8080, File(“.”));するだけで8080番で現在のディレクトリをdocument rootにしたhttpdが起動する。

カスタムするにはサンプルと同じくNanoHTTPDクラスを継承して、関数を自分で上書きしてしまえばいい。
主にserve(String uri, String method, Properties header, Properties parms, Properties files)を上書きする事になると思う。

本体のコードを見るとserveからserveFile関数を呼び出しているので、自分でserve関数を定義し直したらそこからfileServeも呼び出しなおしてあげないとファイル配信できなくなる。
(同梱されていたサンプルのHelloServer.javaではfileServeが動いていない)


webブラウザからMacを遠隔操作する例



サンプルのHelloServer.javaを改造してみた。特定のパス(/goと/stop)にリクエストが来たらMacのsayコマンドで「ゴー」「ストップ」と喋らせる。
それ以外のパスへのリクエストは、dataディレクトリ内のファイルを返すようにしてみた。
Androidのプログラムに埋め込む時は、SDカードやassetsのディレクトリでファイル配信すればいいと思う。
import java.io.*;
import java.util.*;

public class HelloServer extends NanoHTTPD
{
public HelloServer() throws IOException
{
super(8080, null);
}

public Response serve( String uri, String method, Properties header, Properties parms, Properties files )
{
System.out.println( method + " '" + uri + "' " );
if(uri.equals("/go")){
try{
Runtime.getRuntime().exec("/usr/bin/say go");
}catch(java.io.IOException e){
System.err.println(e);
}
System.out.println("go!!!!");
return new NanoHTTPD.Response( HTTP_OK, MIME_HTML, "go");
}
if(uri.equals("/stop")){
try{
Runtime.getRuntime().exec("/usr/bin/say stop");
}catch(java.io.IOException e){
System.err.println(e);
}
System.out.println("stop!!!!");
return new NanoHTTPD.Response( HTTP_OK, MIME_HTML, "stop");
}
return serveFile(uri, header, new File("./data"), true);
}


public static void main( String[] args )
{
try
{
new HelloServer();
}
catch( IOException ioe )
{
System.err.println( "Couldn't start server:" + ioe );
System.exit( -1 );
}
System.out.println( "Listening on port 8080. Hit Enter to stop." );
try { System.in.read(); } catch( Throwable t ) {};
}
}


dataというディレクトリを作ってその中にindex.htmlとして保存する。
ボタンが2つ並んでるだけだけど、押したらMacが「ゴー」「ストップ」とか喋る。
<html>
<head>
<script src='//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js' type='text/javascript'></script>
<script type="text/javascript">
$(function(){
console.log("start");

$("#go").click(function(){
console.log("go button");
$.get("/go");
});
$("#stop").click(function(){
console.log("stop button");
$.get("/stop");
});

});
</script>
</head>
<body>
<h1>remote controll</h1>
<div>
<input type="button" value="go" id="go"></input>
<input type="button" value="stop" id="stop"></input>
</div>
</body>
</html>

0

HTML+JavaScriptをArduinoに直結できるシリアルポートサーバーを作った

SerialPort Serverを使うと、HTMLとJSをArduinoに直結できる。JavaScriptを少し書くだけでArduinoに「カーテン開けろ」とか「部屋の明るさよこせ」とか命令を送れるわけだ。
Web系の技術とハードウェアの技術を同時に使うには、間に「つなぎ」が必要なので、必要な機能を全部入れたサーバーを作ってみた感じです。
(github pagesでプロジェクトページ作ってみたんだけど楽でいいですね)


こういうこと。


シリアルポートサーバーはrubygemsでインストールできる。

gem install serialport-server
which serialport-server
serialport-server --help
serialport-server /dev/tty.デバイス名
–helpでヘルプが出る。デバイス名を引数にして起動できる。
Macならsudo gem installで一発でインストールできるはず。

起動するとHTTPサーバー(http://localhost:8783)、WebSocketサーバー(ws://localhost:8784)、TCP Socketサーバー(localhost:8785)の3つが同時に起動する。

HTTPのサーバーはajaxのクロスドメイン制限を超えてデータのやりとりができるようにしてある。シリアルポートサーバーを動かして、HTMLとJavaScriptを書けばArduinoとJavaScriptが直接通信できるというわけだ。
(response headerにAccess-Control-Allow-Originを付ければ良いとyuisekiのはてブを見ていて知ったのでやってみた。)


■デモ
SerialPort Serverのページにも英語で書いたが、簡単なサンプルプロジェクトを日本語でも解説しておく。


1. Arduinoにプログラムを書き込む

2. ArduinoのDigital13番ピンにLEDを、Analog0番ピンにCdSと10kΩの抵抗を接続する
SerialPort Server sample
SerialPort Server sample

3. SerialPort Serverを起動する

4. Ajax版サンプルを開いて試してみる

5. WebSocket版サンプルを開いて試してみる


WebブラウザからLEDを点灯消灯したり、CdSの明るさの値がリアルタイムにスライダーに反映されているはずだ。
実際にAjaxサンプルのJavaScriptのコードを見てみると簡単さがわかると思う。17行でLEDとCdSを制御できている。



もちろんWebサーバーなので、複数のWebブラウザがSerialPort Serverから同時にデータを読み出そうとしても大丈夫。
増井研で1年以上、遠隔操縦ロボットの制御に使っていたプログラムを元に使っているのでプロセスを起動してたぶん3ヶ月ぐらい放置していても動き続けるぐらいに安定していると思う。

0

Rubyでcometサーバー作る

最近cometとかいう最新技術が流行っているらしいので、eventmachine_httpserverで作ってみた。


ここにサーバーとクライアントの例がある。どっちも50行ぐらいで実装できた。
comet at master from shokai/eventmachine-study – GitHub

サーバー起動して、タイムアウトを10秒に指定。
このサーバーは、POSTされた値を保持して、GETされたら返す。GETに対してはレスポンスを遅らせて返す。

ruby server.rb 8080 10


クライアントを起動。GETしてから25秒後にPOST
ruby client.rb 25
GET(comet) -> wait 25 sec -> POST
* GET
sleep 25 sec
404 ## 10秒経過、切断された

* GET ## 再接続
404

* GET ## 3回目
* POST kazusuke
POST success
200
kazusuke
200 ## 5秒待ってようやく値が返ってきた
kazusuke
* GET

サーバー側のログはこんなんなってた
http server start, port:8080, comet_timeout:10(sec)
load: 2.20 cmd: ruby 11481 waiting 0.45u 0.32s
request_method : GET
path_info : /message
query_str :
post_content :
request_method : GET
path_info : /message
query_str :
post_content :
request_method : POST
path_info : /message
query_str :
post_content : kazusuke
kazusuke
request_method : GET
path_info : /message
query_str :
post_content :

cometサーバー、接続が不安定なクライアントにpush通知するのに便利。

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便利だなー