Posted filed under TitaniumMobile.

 この記事は、@astronaughtsさんの「Titanium Advent Calendar 2011」企画で書いています。

 2011年もTitnium Mobileを使って頂きありがとうございました!まだ使ってない方は、この年末のお休みにでも、ぜひ試してみてください。来年は、日本法人設立など前半から大きな動きがあると思いますので、来年も引き続きよろしくお願いします。

 さて、本題ですが、ソケット通信の話になります。Titanium Mobile(以下Ti)では1.7からTCP通信の機能が充実しました。

 いままでTiの通信と言えばHTTPでしたが、これからはTCPソケットを使って、色々なサービスと通信することができるようになりました。

 ソケットと同時に、Tiでバイナリを扱うための、T.BufferTi.Codecをサポートされ、ちょっと面倒ですがHTTP以外のプロトコルもバリバリ実装することができます。

 早速、ソケット通信の簡単な例を見ていきましょう。

var win = Ti.UI.createWindow({ title: "Win1" });
var textarea = Ti.UI.createTextArea({value: ''});
win.add(textarea);
win.open();

// 読み込み用バッファ
var readBuffer = Ti.createBuffer({length:1024});

// ソケット本体
var socket;
socket = Ti.Network.Socket.createTCP({
  host: "blog.masuidrive.jp",
  port: 80,
  connected: function(e) { // ソケットに接続された
    textarea.value += ">> Connected to host " + socket.host + "n";
    // 書き込むためのバイナリを作る
    var data = Ti.createBuffer({value:"GET /tmp/test.html HTTP/1.1rn"+
      "Host: blog.masuidrive.jprnrn"});
    // ソケットに書き込む
    var bytesWritten = socket.write(data);
    
    var bytesRead;
    // 終わるまで読み込みループ
    while( (bytesRead = socket.read(readBuffer)) >= 0 ) {
      // 読み込んだバイナリを文字列に変換
	    var stringData = Ti.Codec.decodeString({
	      source: readBuffer, length: bytesRead });
  	  textarea.value += bytesRead + "> " + stringData + "n";
  	  // 読み込みバッファをクリア
  	  readBuffer.clear();
    }
    // ソケットを閉じる
    socket.close();
  },
  closed: function(e) { // ソケットが閉じられた
    textarea.value += ">> Socket closed";
  }
});
socket.connect();


 同期IOでは、上記のように書きます。ソケット通信の詳細は省きますが、他環境でソケットを扱ったことのある人であれば、分かると思います。ここまでは本家のブログでも解説されています。

 実際にソケットプログラムをするときは、ほとんど非同期IOになると思います。しかし、本家記事では書いてない!と言うことで、

 非同期IOでの書き方は下記の様になります。

var win = Ti.UI.createWindow({ title: "Win1" });
var textarea = Ti.UI.createTextArea({value: ''});
win.add(textarea);
win.open();

var socket;
var readBuffer = Ti.createBuffer({length:1024});

// データが読み込まれたときに呼ばれる
var readCallback = function(e) {
    // データが終了であれば、ソケットを閉じる
    if (e.bytesProcessed == -1) { // EOF
    	textarea.value += ">> Received socket closedn";
    	socket.close();
        return;
    }

    // 読み込んだデータを表示
    var str = Ti.Codec.decodeString({source:readBuffer, length:e.bytesProcessed});
    textarea.value += e.bytesProcessed + "> " + str + "n";

    // 次のデータが読み込まれる時のバッファとコールバックを設定
    Ti.Stream.read(socket, readBuffer, readCallback);
};

socket = Ti.Network.Socket.createTCP({
    host: "blog.masuidrive.jp",
    port: 80,
    connected: function(e) {
        // データが読み込まれる時のバッファとコールバックを設定
	    Ti.Stream.read(socket, readBuffer, readCallback);
	    
        textarea.value += ">> Connected to host " + socket.host + "n";

        // 書き込み処理
        var data = Ti.createBuffer({value:"GET /tmp/test.html HTTP/1.1rnHost: blog.masuidrive.jprnrn"});
        var bytesWritten = socket.write(data);
    },
    closed: function(e) {
        textarea.value += ">> Socket closed";
    }
});
socket.connect();

 今度はデータが読み込まれる度に、readCallbackが呼ばれるようになりました。これを駆使する事で、TCPを使うほぼ全てのプロトコルを実装することができます。

 私はこれを使って、WebSocketプロトコルを実装してみました。ソースは、https://github.com/masuidrive/ti-websocket-clientで公開しています。

 リアルタイムチャットなど、長時間のセッションを張りたい時にはHTTPを使えないので、このWebSocketを使う事で実現することができるようになります。

 手元ではSocketIOでも動くようになっていて、本家にコミットしたいのですが、なんかSocketIO方向で大きな動きがあったので、色々追いかけないと行けないみたい orz

 ソフトバンクの回線は80番ポートの通信にTransparency Proxyが入ってるようで、WebSocketなどHTTP 1.1以外のプロトコルが通ると一部通信内容がフィルタされるようです。iPhoneで通信をする場合は、80番ポート以外を使うことをオススメします。

 明日は、ryugoo_さんです!よろしくお願いします!!

3 Responses to “【Titanium Advent Calendar 2011:五日目】Titanium Mobileでソケット通信”

  1. Ken OKABE

    ありがとうございます。非常に参考になりました。

    http://developer.appcelerator.com/apidoc/desktop/latest/Titanium.Network.HTTPServer.bind-method.html
    このサンプルは、デスクトップバージョンのみで、モバイルで動かないようですが、
    モバイルでHTTPサーバーを実装するにはどうすればよいでしょうか?

    https://gist.github.com/1156622
    このコードを見つけたのですが、問題があるとのことで、何かアドバイスいただけたら有難いです。
    どうぞよろしくお願いします。

    • masuidrive

      お返事遅れて済みませんでした。
      Ti mobileには、http serverの機能がありません。なのでTCPServerを使って実装するか、moduleを使うことになります。
      TCPServerを使ってhttpをフル実装するのは、相当大変なので、moduleで作る方が良いと思います。

  2. Ken OKABE

    お返事どうもありがとうございます。参考にさせていただきます。