最近は地味に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です。
メインマシンをWindows 7にしました。
Vistaの良さを引き継ぎつつ、動作が軽快になって、かなり良いWindow 7ですが、まだRCなのでたまに問題が起こります。
今日は、再起動したらネットワーク接続がおかしくなりました。
IPアドレスは取得できているし、ルーターまでは接続できるのに、外につながりません。
これは、デフォルトゲートウエイの設定が間違っていたのが原因でした。
本来の値(192.168.100.1)のほかに、謎の0.0.0.0が追加されていました。
DHCPのあたりにバグがあるのか、ルーターとの相性問題が発生したのかもしれません。
以下、内側ネットワークを192.168.100.0/24, デフォルトゲートウエイを192.168.100.1と仮定します。
【問題が発生していた状態】
> ipconfig
IPv4アドレス: 192.168.100.205
サブネット マスク: 255.255.255.0
デフォルト ゲートウエイ: 0.0.0.0
192.168.100.1
ネットワークアダプタのプロパティで設定しようとしたのですが、DHCPとうまく両立して手動設定する方法が見つからなかったので、コマンドプロンプトを使用しました。
> route delete 0.0.0.0
> route add 0.0.0.0 MASK 0.0.0.0 192.168.100.1
【正常な状態】
> ipconfig
IPv4アドレス: 192.168.100.205
サブネット マスク: 255.255.255.0
デフォルト ゲートウエイ: 192.168.100.1
これで、正常に外部に接続できるようになります。
注意点としては、通常のコマンドプロンプトで実行すると権限エラーが出るので、コマンドプロンプトを「管理者として実行」するか、runasコマンドを使うことです。
XAMPPでApacheが起動しなくてちょっと困りました。
XAMPP Control PanelでApacheの「起動」を押しても、瞬時に終了してしまいます。
コマンドプロンプトで
C:\xampp\apache\bin>apache.exe
を実行したら、httpd.conf (C:\xampp\apache\conf\httpd.conf) が間違ってるというエラーが出てきました。(ServerRootとDocumentRootを間違えた)
いや、書き間違える僕が悪いんですが、終了する前にエラーを出して欲しかった。
コマンドプロンプトから実行したときだけ、ちゃんとエラーを見せてくれます。
Ethnaは標準でXML-RPCに対応しているので、
# ethna add-action-xmlrpc hoge
とやるだけで app/action-xmlrpc にActionのテンプレートが作れますし、クライアントからHogeメソッドをコールすると、自動的にHogeアクションが実行されます。
(エントリポイントはindex.phpではなくxmlrpc.phpになります)
しかし、デフォルトでは引数がActionFormのform定義と順番に1対1で対応するため、引数が多くなると使いづらくなります。
ここは、PHPらしく引数名をキーにした連想配列にしたいところ。
(プロジェクト名)_Controller に以下のメソッドを定義(オーバーライド)することで、XMLRPCの第一引数を連想配列として扱い、対応する名前のformに値がセットされるようになります。
function trigger_XMLRPC($method, $param)
{
// アクション定義の取得
$action_obj =& $this->_getAction($method);
if (is_null($action_obj)) {
return Ethna::raiseError("undefined xmlrpc method [%s]", E_APP_UNDEFINED_ACTION, $method);
}
// オブジェクト生成
$backend =& $this->getBackend();
$form_name = $this->getActionFormName($method);
$this->action_form =& new $form_name($this);
$def = $this->action_form->getDef();
/***** ここから変更 *****/
if (isset($param[0]) && is_array($param[0]))
{
foreach ($param[0] as $key => $value)
{
if (isset($def[$key]))
{
$this->action_form->set($key, $value);
}
}
}
/***** ここまで変更 *****/
// バックエンド処理実行
$backend->setActionForm($this->action_form);
$session =& $this->getSession();
$session->restore();
$r = $backend->perform($method);
return $r;
}
これで、クライアント側では引数の順番を気にせずに済みます。
家族のPCにNorton Internet Security 2009体験版を入れてみたところ、Shurikenでのメール送受信が出来なくなりました。
POP3S/SMTPSは使えるのですが、POP/SMTPが使えない状態です。インストール直後は平気だったのに、ウィルススキャンをしたらだめになったとか。
110番と25番許可にしたり、ファイアウォールの一番上に全部許可のルール入れたり、アプリケーション制御で信頼するアプリに登録したり、ファイアウォールオフにしたり、ノートン先生丸ごとオフにしたり、色々やっても解決せず、
再起動したら直りました。謎。
やっぱり僕はKasperskyが好きです。高いけど。
C#でスレッドを使うときの注意です。
System.Windows.Forms.Timer はお手軽でよく使いますが、このTimerはUIスレッドでのみ動くためなのか、別スレッドから呼ぶと動かないみたいです。
System.Threading.Thread や System.Threading.Timer、TcpListener::BeginReceive() などで作ったスレッド内で System.Windows.Forms.Timer を作ってStart()メソッドを呼んでも、Tickはいくら待っても呼ばれません。
//テストコード
Thread thread = new Thread(new ThreadStart(delegate {
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer { Interval = 1000 };
timer.Tick += new EventHandler(delegate { MessageBox.Show("HOGE");});
timer.Start();
}));
thread.Start();
上記のテストコードで、メッセージボックスは永久に出ません。
System.Windows.Forms.Timer を System.Threading.Timer に変えれば動くので、これは System.Windows.Forms.Timer の制約のようです。
ネットワークでパケット受信をトリガに処理を始めるときなど、うっかりミスしないように気をつけましょう。
C#のTcpListenerを使って、サーバを作っていています。
System.Net.Sockets.Socket.Available プロパティは、信用してはいけません。
下のコードは、クライアントから接続があってもたまにしか受信できません。
using (Socket client = listener.AcceptSocket())
{
//ここにThread.Sleep(1000)とかを入れるとうまく動くことも多い・・・
while (client.Available) //ここでいきなりfalseが返ってくる
{
byte[] buf = new byte[256];
client.Receive(buf);
Console.WriteLine(Encoding.ASCII.GetString(buf));
}
}
この場合、次のようにするのが正しいです。
using (Socket client = listener.AcceptSocket())
{
byte[] buf = new byte[256];
int bytes;
while ((bytes = client.Receive(buf)) > 0)
{
Console.WriteLine(Encoding.ASCII.GetString(buf));
}
}
それから、TcpListenerのコンストラクタは第一引数にローカルIPアドレス、第二引数にポート番号を渡しますが、サーバに置いては
TcpListener listener = new TcpListener(IPAddress.Any, 45678);
のように IPAddress.Any を渡すのが正しいです。「ローカルIPアドレス」といっても、サーバマシンのアドレスでは無く、クライアントのアドレスを渡します。わかりにくい。。
仮想サーバのNICに負荷をかけるとホストマシンごと落ちる件について、現状改善したように見えるので、解決記録を書いておきます。
まず蟹が原因と思い、ヨドバシカメラにNICを買いに行きました。新春セールでポイント5%アップで幸せ気分です。
本当はIntelが欲しかったけど売ってなかったので、玄人指向のGBE-PCIe(Marvell 88E8053)とGbE-PCI2(VIA VT6122)を買ってきました。PCIeが良いのでMarvellの方を使うつもりで、VIAは予備用に買いました。
早速Marvellに差し替えたところ、負荷をかけてもホストOSは落ちなくなりました。やっぱり蟹が原因だったみたいです。
(ちなみに、NICのMACアドレスが変わった場合、Ubuntuの場合 /etc/udev/rules.d/70-persistent-net.rules を編集すれば eth0 に割り当てるなど出来ます)
しかし、ホストOSは落ちないものの仮想サーバの通信は負荷をかけるとやっぱり切れてしまいます。
調べてみると、eth0: out-of-sync dirty pointer というエラーを出していました。また、ifconfigをみるとエラーがたくさんあります。
そこで、使っていたBUFFALO製のスイッチングHUBを外して、仮想サーバ用のMarvellとRTX1000を直接つないでみたら、負荷をかけてもエラーが出なくなりました。
スイッチングHUBをcorega製に変えてみても、エラーは出ませんでした。
つまり、RealtekのNICとBAFFALOのHUBが両方悪かったみたいです。
値段だけで選ぶと痛い目に遭いますね。