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日追記
詳しい方から、「仕様です」との情報を頂きました。
非同期通信を使って、タイムアウトは自分で処理する必要があるそうです・・・
最近は地味にiPhone SDKで試行錯誤中です。
基本的な内容ばかりになると思いますが、気づいた点を書いていこうと思います。
今日はCookieについて。
まず、一番単純なHTTPリクエストは、以下のようなコードで実行できます。
(実際は同期通信だと色々まずいので、非同期通信を使うことが多いと思います)
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.bpsinc.jp"]];
NSURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//結果を表示
NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@", str);
この際、サーバ側でセッションを開始するなどしてCookieの発行を指示した場合、レスポンスヘッダにSet-Cookieが入ります。
これを受け取ると、NSURLConnectionが自動的にCookieを保存してくれます。
そのため、次回同じURLにリクエストした場合、保存されているCookieが送信されます。
このCookieを取得するには、以下のような手順を踏みます。
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookies = [storage cookies]; //cookiesForURLで絞り込みもOK
for (int i = 0; i < [cookies count]; i++) {
NSHTTPCookie *cookie = [cookies objectAtIndex:i];
[storage deleteCookie:cookie]; //削除する
}
これで、セッションの強制的な解除などが実現できます。
NSHTTPCookieStorageに保存されているCookieは、Safariなどと共有されているらしいです。
このあたりはもう少し実験してみようと思います。
C#でHTTPサーバを作るのはすごく簡単、
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://*:8000/"); //ポート番号は好みで。80でもOK
listener.Start();
while (true)
{
HttpListenerResponse response = listener.GetContext().Response;
response.OutputStream.Write(data, 0, (int)data.Length); //ここで何かdataを出力する
response.Close();
}
のようなコードだけで出来てしまいます。
しかし、これだとWindows Vista / 7 のUAC有効環境で、HttpListenerExceptionが発生します。
Windowsファイアウォールを無効にしても結果は変わりません。
UAC有効の状態では、通常権限のプログラムは、localhost以外からの接続を受け付けられないようです。
そもそもテストやミニプログラム以外でHttpListenerを使うか疑問が大きいので、手抜きで安直な解決方法を探ってみます。
localhostに限定
ローカルでテストする分には、
listener.Prefixes.Add(”http://localhost:8000″);
のようにすればとりあえず動きます。
管理者権限にする
localhost以外からも接続させたいなら、管理者権限で動かしてしまうのが簡単です。
Visual Studioを起動する際に管理者権限にしておけば、デバッグで起動するプログラムも管理者権限になるので、例外は発生しなくなります。
Dojoは、デフォルトでは読み込み元HTMLと同じサーバに置くことを前提に作られています。
具体的には、dojo.require()が、XMLHttpRequestを使ってデータを取得します。
このため、同じドメインにライブラリを設置できない場合(共有サーバで権限が無い時など)、クロスドメインの制限でdojo.require()が失敗してしまいます。
dojo.parserなどの内部でrequireを使っているため、ライブラリの読み込み時点でエラーが多発してしまいます。
この場合は、クロスドメインビルドをしたdojoライブラリ(XDomain Dojo)を使用します。
方法1: AOLを使用
お手軽に使うには、AOLのCDNを活用します。
http://dev.aol.com/dojo に書いてあるように、以下の形式でdojoライブラリを読み込むだけでOKです。
<script type=”text/javascript” src=”http://o.aolcdn.com/dojo/1.3/dojo/dojo.xd.js”></script>
この場合、dojo.requireは、デフォルトで用意されているdojo, dijit, dojoxのみ使用可能で、自前のコンポーネントは使用できません。
方法2: クロスドメインDojoのビルド
方法1はお手軽ですが、AOLのサーバが重いとか、自前で設置したいこともあると思います。
その場合、Dojoをソースからビルドします。
-
ソースを入手
まず、Dojoのダウンロードサイトから、ソースを取得します。dojo-release-1.3.0-src.zip のように、srcが付いているものがソースです。
ダウンロードサイト: http://download.dojotoolkit.org/
- ビルド
そして、以下のページの手順に従い、ビルドします。
ビルドの手順: http://www.dojotoolkit.org/book/dojo-book-0-9/part-4-meta-dojo/package-system-and-custom-builds
具体的には、以下のコマンドでビルドできます。(Linuxの場合)
xdDojoPathに、Dojoライブラリを設置するディレクトリ名を記述します。テスト環境と本番環境ではURLが違うと思うので、その場合は2回ビルドする必要があります。
パックするライブラリを変更したい場合は、profileを変更します。
$ cd util/buildscripts
$ build.sh profile=standard loader=xdomain xdDojoPath=http://www.bpsinc.jp/dojoxd action=release
以上のコマンドを入力すると、ビルドが始まり、しばらくするとreleaseフォルダにXDomainビルドされたDojoが生成されます。
- 設置
xdDojoPathに設定したフォルダに、dojoを設置します。
上記の例では、DocumentRootのdojoxdフォルダに、dojo/dijit/dojox が入っている状態になります。
- 読み込み
あとは、
<script type=”text/javascript” src=”http://www.bpsinc.jp/dojoxd/dojo/dojo.xd.js”></script>
のように読み込めばOKです。