Railsで開発する際は、素直にRadRailsのボタンでサーバを起動してしまうのが楽で良いです。
例:http://localhost:3000
しかし、ポート番号ではなく好きな名前でアクセスしたいこともあります。
また、Rails 2.3には、prefix(ドメイン直下以外にアプリを配置)にバグがあるため、それをローカルで試したいこともあるかもしれません。
XAMPPだけで普段はサーバ作業に触れない人には馴染みが薄いかもしれませんが、Apacheなので、たんなるProxyならかんたんです。
例:http://localhost/myapp
<VirtualHost *:80>
ProxyRequests Off
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
ProxyPass /myapp http://localhost:3000
ProxyPassReverse /myapp http://localhost:3000
</VirtualHost>
なお、肝心のPrefixバグの対策は、システムチームの伊藤がブログを書いていますのでご覧下さい。
http://www.bpsinc.jp/blog/archives/1579
Androidで、URLを指定してInputStreamを受け取りたいとき、
URL url = new URL("http://example.com");
InputStream is = url.openConnection().getInputStream();
とできますが、なぜかAndroid 2.1では動き、1.6ではFileNotFoundExceptionが発生しました。
原因究明したいところですが、とりあえずHttpClientに置き換えれば動きます・・・
HttpClient client = new DefaultHttpClient();
HttpGet method = new HttpGet(URI.create("http://example.com"));
HttpResponse response = client.execute(method);
InputStream is = response.getEntity().getContent();
ActiveDirectoryを使う環境では、DNSサーバがSRVレコードをサポートしている必要があります。
(通常のDNSだとDomainControllerを検索する際にエラーになってしまいます)
BINDなどのDNSサーバで設定することも可能ですが、かなり面倒なので、Windows ServerをプライマリDNSにしてしまうのが手っ取り早いですね。
ところが、Windows ServerをプライマリDNSにしているActiveDirectory環境で、LANから公開サーバを検索しようとしたら、少しだけ困ったことになりました。
公開サーバ(WAN/LANの2つのIPを持つ)の名前をwwwとすると、
DNSサーバ:192.168.1.50
公開サーバ:192.168.1.10 , 203.143.124.180
期待する結果
C:> nslookup www.bpsinc.jp
サーバー: UnKnown
Address: 192.168.1.50
名前: www.bpsinc.jp
Address: 192.168.1.10
残念な結果
C:> nslookup www.bpsinc.jp
サーバー: UnKnown
Address: 192.168.1.50
名前: www.bpsinc.jp.bpsinc.jp
Address: 203.143.124.180
このように、bpsinc.jpのサフィックスが無駄にくっついてしまいます。
これは、ネットワーク設定→TCP/IP(IPv4)設定→詳細設定→DNS で、以下のようにDNSサフィックスを手動設定すれば直ります。

ローカルでここを変更してもOK
グループポリシーで一括設定するには、コンピュータの構成→ポリシー→管理用テンプレート→ネットワーク→DNSクライアント→DNSサフィックス検索一覧 で設定すればOKです。

グループポリシーでDNSサフィックス設定
もちろん、www.bpsinc.jp. のように最後にピリオドをつければ何もしなくてもOKなのですが、
WindowsのFQDN判定はあまり賢くないということでしょうか?
先日のiPhone UDP受信テストの際に使った、WindowsからUDPパケットを送りつけるプログラムを載せてみます。
一切の遠慮無く、1秒に20枚のペースで、デスクトップに置いてあるimg0.png~img9.pngの画像ファイルを一方的に送りつけます。
///一定時間ごとにUDPで画像を一方的に送りつける
private void SendData()
{
//ローカルポート番号2222にバインドする
System.Net.Sockets.UdpClient udp = new System.Net.Sockets.UdpClient(2222);
int count = 0; //何番目の画像を送信するかのカウンタ
Timer timer = new Timer();
timer.Tick += new EventHandler((s, e) =>
{
if (++count > 9)
{
count = 0;
}
//デスクトップのimg0.png~img9.pngを送る
string filename = @"C:\Users\baba\desktop\img\" + count + ".png";
using (System.IO.FileStream fs = new System.IO.FileStream(filename, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
byte[] sendBytes = new byte[fs.Length];
fs.Read(sendBytes, 0, sendBytes.Length);
//宛先IPとポート番号を指定する
udp.Send(sendBytes, sendBytes.Length, "192.168.100.50", 5555);
}
});
timer.Interval = 50;
timer.Start();
}
.NETは簡単にこういったコードが書けて便利ですね。
何の工夫も最適化も無いですが、50枚/秒くらいなら全然問題にならない程度の速度は出ていました。
先日のiPhone性能検証で作ったソースの一部として、超手抜きUDPサーバのコードを載せておきます。
CFSocketが思ったよりも使いにくいので、BSDソケットほとんど生です・・・
/**
* 手抜きUDPサーバ
*/
#import <netinet/in.h>
#import <Foundation/Foundation.h>
@interface MyUdpConnection : NSObject {
id delegate;
}
- (id)initWithDelegate:(id)_delegate; //receiveData:(NSData*)data を実装すること
- (void)bind;
- (void)bindThread;
@end
/**
* 手抜きUDPサーバ実装部
*/
@implementation MyUdpConnection
// 初期化
- (id)initWithDelegate:(id)_delegate {
delegate = _delegate;
return self;
}
// 受信開始
- (void)bind {
NSThread *th = [[NSThread alloc]initWithTarget:self selector:@selector(bindThread) object:nil];
[th start];
}
// 受信用スレッド
- (void)bindThread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_port = htons(5555); //適当なポートで待機
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
int sock = socket(AF_INET, SOCK_DGRAM, 0);
bind(sock, (struct sockaddr *)&addr, sizeof(addr));
char buf[100000]; //100KBまで対応
while (1) {
//ここでデータを受信するまでブロックされる
int size = recv(sock, buf, sizeof(buf), 0);
//NSDataに変換し、delegateに通知
NSData *data = [NSData dataWithBytes:buf length:size];
[delegate performSelectorOnMainThread:@selector(receiveData:) withObject:data waitUntilDone:YES];
}
[pool drain];
}
@end
これを使うと、たとえばViewControllerなどで
- (void)receiveData:(NSData *)data;
を実装しておいて、
MyUdpConnection *udp = [[MyUdpConnection alloc]initWithDelegate:self];
[udp bind];
のようにすれば、データを受信したときにreceiveData:が呼ばれます。
エラー処理や再送処理はおろか、ポート番号すらべた書きのものですが、重要でないデータを受け取る分には、ちょっと改良すれば使い物になりそうです。
iPhoneやiPod Touchは、ネットワークの性能がそれほど高くないため、NSURLConnectionでたくさんコネクションを張ったりすると目に見えて遅延します。
以下のような実験をやってみました。
iPhone SDKで、以下の2種類のテストアプリを作りました。
なお、機材の都合上iPhoneではなくiPod Touchを使っています。
【A】
タイマーで0.05秒ごとにHTTPリクエストを送信し、レスポンスとして受け取った画像を表示する。
サーバでは、リクエストを受け取るたびに0~9の数字画像を順番に返す。
【B】
UDPで接続を待ち、画像データを受け取ったら表示する。
サーバでは、iPhoneに向かって0.05秒ごとに0~9の数字画像を順番にUDPで送信する。
サーバは、十分に高性能なマシンを、有線LANで接続しました。
MacBook ProやiPod Touch実機は、同じ無線LAN (IEEE802.11g) を使っています。
【結果】
Aのアプリ(MacBook Pro上のシミュレータ)
Aのアプリ(iPod Touch 実機)
Bのアプリ(iPod Touch実機)
このように、HTTPを使う場合、シミュレータではスムーズに動くものの、実機ではかなりカクカクです。
実機でも、HTTPを使わずにUDPにしてしまえば、十分スムーズになることが分かります。
今回使った画像は各2KB程度のPNG画像ですが、50KB程度のJPG画像を使っても結果はほとんど変わらなかったため、帯域よりもコネクションの数が大きなウェイトを占めているようです。
どの程度性能が出るか大まかに分かれば良かったので、原因などの細かい検証は今回は行いません。
使ったソースコードなどは後日このブログに載せておこうかと思います。
iPhoneでXML_RPCを使おうとすると、地味に苦労します。
XMLParserはあるので、自力でやっても良いのですが、出来ればライブラリが欲しいですよね。
eczarny xmlrpcがiPhone対応ということですが、私の環境ではターゲットをiPhone OSにするとビルドエラーが出ました。
MacOSをターゲットにすると正常にビルドできます。NSXMLDocumentなどを内部で利用しており、これがiPhone非対応なのが原因みたいです。2.0~2.2.1まで全滅でした。
ということで他を当たったところ、WordPress for iPhoneの内部で使われているものが良さそうでした。
これもeczarmy xmlrpcをベースに作られているようですが(未確認)、iPhoneでもちゃんと動きます。
ということで、以下からソースコードをダウンロードします。ライセンスはGNU GPLでした。
http://iphone.trac.wordpress.org/browser(trunkを開いて、下の方にあるZip Archiveをクリック)
色々入っていますが、Classes/XMLRPCフォルダ丸ごとと、Classes以下でファイル名に「+」が含まれるものを全部コピーして、プロジェクトにインポートすればOKです。

このようにインポートします
使い方は、以下のようにやります。
#import "XMLRPCRequest"
#import "XMLRPCResponse"
#import "XMLRPCConnection"
- (void)test {
//requestを生成
NSURL *url = [NSURL URLWithString:@"http://www.bpsinc.jp"];
XMLRPCRequest *request = [[XMLRPCRequest alloc]initWithHost:url];
[request setMethod:@"Test.version" withObjects:[NSArray array]]; //引数を指定するときはarrayの部分にデータを入れる
NSLog(@"%@", [request source]); //XMLRPCのリクエストソースが見られます
//通信
XMLRPCResponse *response = [XMLRPCConnection sendSynchronousXMLRPCRequest: request];
NSLog(@"%@", [response source]); //XMLRPCのレスポンスソースが見られます
NSLog(@"%@", [response object]); //objectメソッドは、戻り値の型になります。サーバ側で明示的に型を指定しましょう
}
ソースコードは短く読みやすいので、簡単に使いこなせると思います。
非同期通信は、以下の2つを実装すれば、XMLRPCConnectionのinitWithRequestで実行できます。
- (void)connection:(XMLRPCConnection *)connection
didReceiveResponse:(XMLRPCResponse *)response forMethod:(NSString *)method;
- (void)connection:(XMLRPCConnection *)connection
didFailWithError:(NSError *)error forMethod:(NSString *)method;
XML_RPCのクライアントを開発するとき、ダミーでサーバが必要になります。
手っ取り早いダミーとして、PHPのPEAR::XML_RPC2が便利です。以前クライアント側は紹介しましたが、今回はサーバの超基本的な使い方。
ダウンロード
http://pear.php.net/package/XML_RPC2からダウンロードできます。
使い方
適当な場所に設置したら、以下のようにincludeして、各アクションに対応する関数を定義します。
そして、XML_RPC_Serverでアクション名と実行するアクションの対応を配列で渡せばOKです。
require_once("XML/RPC.php");
require_once("XML/RPC/Server.php");
$GLOBALS['XML_RPC_defencoding'] = "UTF-8";
function version($params) {
return new XML_RPC_Response(new XML_RPC_Value('1.0', 'string'));
}
function average($params) {
$v1 = $params->getParam(0)->scalarval();
$v2 = $params->getParam(1)->scalarval();
return new XML_RPC_Response(new XML_RPC_Value($v1 + $v2, 'int'));
}
new XML_RPC_Server(array(
"Test.version" => array("function" => "version"),
"Test.average" => array("function" => "average"),
));
とってもかんたん。
先日に引き続き、timeoutIntervalが無視される件について。
POSTする際は、NSMutableURLRequestを用いて、setHttpBodyでPOSTデータを入れます。
しかし、これを行うとtimeoutIntervalが強制的に240秒に設定されてしまいます。
さらに酷いことに、その後setTimeoutIntervalを行っても無視されます。
以下検証コード↓
NSURL *url = [NSURL URLWithString:@"http://www.bpsinc.jp"];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
NSLog(@"%g", [req timeoutInterval]); //60
[req setHTTPBody:[NSData data]];
NSLog(@"%g", [req timeoutInterval]); //240
[req setTimeoutInterval:10.0];
NSLog(@"%g", [req timeoutInterval]); //240
これはCocoaのバグでしょうか?
あまりに酷すぎるので、何か基本的な間違いを犯していると信じたいところです。
どなたか情報をお持ちなら教えて下さい!
1月14日追記
詳しい方から、「仕様です」との情報を頂きました。
非同期通信を使って、タイムアウトは自分で処理する必要があるそうです・・・