病みつきエンジニアブログ

機械学習、Python、Scala、JavaScript、などなど

Objective-C でWebSocketの通信をする(SocketRocket)

今流行(?)のWebSocketちゃんですが、Objective-Cで触るための日本語のサイトがあまりなかったので。
手順としては、node.js でWebSocket のサーバーを作り(not Socket.IO)、SocketRocketを使ってそのサーバーに接続します。
node.js、Objective-Cともに触ったことがある人を対象にした記事です。

WebSocketサーバーを作る

Socket.IOというのがありますが、今回は使いません。というのも、Socket.IOは「ソケット通信っぽいことをするためのライブラリ」で、WebSocketで繋げないブラウザにはLong Pollingやらでソケット通信風のことをするよう、ラッピングしています。そのため、今回のようにWebSocketだけで接続する場合には適していません、というかSocketRocketが対応していません。

さて、WebSocketのライブラリとしてws: a node.js websocket implementationを使います。
nodeのプロジェクト用ディレクトリ内で、いつもどおりにwsをnpmでインストールし、

npm install ws

サーバー用のapp.jsを以下のような感じで作成します。

var WebSocketServer = require('ws').Server
  , wss = new WebSocketServer({port: 8080});
wss.on('connection', function(ws) {
    ws.on('message', function(message) {
        console.log('received: %s', message);
    });
    ws.send('something');
});

これで、ws://localhost:8080/にWebSocketサーバーを立ち上げることができます。
ws.on('message')でメッセージをクライアント(Obj-C)側から受信し、
ws.sendでメッセージをクライアント側に送信することができます。

さて、app.jsを作成したので、例によって例のごとく

node app.js

でサーバーを起動しておきます。

SocketRocketでサーバーにつなぐ

さて、サーバーは完成したので、SocketRocketを使ってWebSocketサーバーにつなぎます。
SocketRocketは、Square(スクエニじゃない方)が開発しているObjective-C用WebSocketのライブラリです。

インストール

https://github.com/square/SocketRocketから、SocketRocketをダウンロードします。(gitがわからない人はhttps://github.com/square/SocketRocket/zipball/masterから)
その中の「SocketRocket」というフォルダが、ライブラリとして使用するフォルダですので、これをXcodeのプロジェクトに追加してあげることで、使用することができます。

ただし、このライブラリは、以下のフレームワークに依存します。

  • libicucore.dylib
  • CFNetwork.framework
  • Security.framework
  • Foundation.framework

なので、これらのフレームワークを追加します。

コード上での使用方法
#import "SRWebSocket.h"

すると、そのソースコード内で、WebSocketを使うことができます。

以下のようにしてSRWebSocketのインスタンスを生成します(Obj-Cおなじみな書き方)。
ここで、つなぐ先は、ws://で始まることに留意して下さい。

SRWebSocket *web_socket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"ws://localhost:8080/"]]];

delegateを使うので、delegateを設定します。

[web_socket setDelegate:self];

インスタンスを作っただけではつながないので、openメソッドで接続します(ちなみに、閉じるときはclose)。

[web_socket open];

Delgateは、以下の4つが使えます。
上から、「メッセージをサーバーから受信したとき」「接続したとき」「接続に失敗したとき」「接続を終了したとき」に呼ばれます。

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
- (void)webSocketDidOpen:(SRWebSocket *)webSocket;
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;

「接続したら、サーバー側にメッセージを送信する」を実装してみます。

- (void)webSocketDidOpen:(SRWebSocket *)webSocket{
  [webSocket send:@"Nexus 7を発送しました"];
}

すると、node.js上に「received: Nexus 7を発送しました」と表示されます。
sendメソッドで、サーバー側にメッセージを送信できます。※sendメソッドは、文字列だけでなく、Dataも送信できます。
Nexus7は10/1に届くらしいです。

次に「サーバーからメッセージを受信したら、NSLogで表示する」を実装してみます。

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message{
  NSLog(@"%@", [message description]);
}

すると、「2012-09-29 13:31:35.896 ProjectName[32523:11303] 目にうつる全てのもの」という感じにNSLogされてるのがわかります。
messageに受信したものが入ってくるので、descriptionメソッドを呼び出してNSLogで出力するなり、JSON用ライブラリに引き渡すなり、なんでもすればいいと思います。

サンプル

僕のサンプルではないですが、SocketRocketに、サンプルが置いてありますので、参考にしてみて下さい。
SocketRocket/TestChat/TCViewController.m at master · square/SocketRocket · GitHub