Help us understand the problem. What is going on with this article?

socket.io × SIOSocketでマルチルーム対応 iOSチャットアプリを作ってみた

More than 5 years have passed since last update.

経緯

前回slackのライブラリを調べた時にwebソケットライブラリ、rocketsocketが入っていて、面白そうだなと思ったので、早速使ってみようかと。webソケットといえばチャット!チャットアプリを作ってみます。

まずは、そのまんまsocketrocketを見てみたんですが、理解ゼロからチャットやろうとすると、結構大変そうだな...と思い、socket.ioにroomsという機能があるのをみつけました。お、これは使えそうだと調べて行ったら、iOSでもwebViewでsocket.ioのクライアントを書いてくれている人がいました。SIOSocketというやつです。これはいけそうだなということで、やってみました。結果、わりと大変でしたが、なんとか動くものはできたかなーとおもったのでアップしてみます。

できたもの

なんかびみょーな仕様ですが...

  • アプリ起動時に自動的に自分のroomに入る
  • 今のところフォアグラウンドでの動作のみ(バックグラウンドでは気が向いたら...)
  • もし他の人もアプリを立ち上げていれば左側のドロワーにでてくるので、そこから選択してjoinできます。同時に複数のroomにjoinできます。

ナビゲーションバーのタイトルの部分がいわゆる部屋の名前です...便宜上socket idにしてるので、無機質極まりないです。名前とか吹き出しの下に出せればよかったです。追々やってみます。
※ ちなみにこのフキダシは JSQMessagesViewController というものをつかっています!使い方は、Developer.IOさんの記事をご参考に。

iOS Simulator Screen Shot Mar 17, 2015, 07.07.15.png

お部屋一覧がみえます。一応ニックネームの変更もできます。
iOS Simulator Screen Shot Mar 17, 2015, 07.05.34.png

用意したもの

()の中は今回利用したversionなど
  • ec2インスタンス (Amazon Linux AMI release 2014.09)
  • node.js (v0.12.0)
  • socket.io (1.3.5)

構成

今回は、iOSアプリ上のSIOSocket <-> ec2上のnodeサーバ with socket.io って感じでやってみます。vagrantだと感動が多少減るので、ec2に置いてリアルインターネットで。

手順

1. nodeサーバを用意

websocket通信ではtcp3000番ポートを使うのでそこだけ開けるように注意

Screen Shot 2015-03-06 at 16.44.45.png

# nodebrewをインストール
wget git.io/nodebrew
perl nodebrew setup

# nodebrewのパスを通す
vim ~/.bashrc
PATH=$HOME/.nodebrew/current/bin:$PATH

# nodeの最新安定版(バイナリ)をインストール
nodebrew install-binary stable
# lsして出てきたものをuse
nodebrew ls
nodebrew use v0.12.0
# 上記versionが出てきたら成功
node -v

# 適当なディレクトリに移動
mkdir ~/SIOChatServer
cd ~/SIOChatServer

# socket.ioのインストール
npm install socket.io

node用サーバにscpでserver.jsを配置

scp -i path/to/your.pem server.js ec2-user@yourhostname:/home/ec2-user/SIOChatServer

あとは実行するだけ。のはず。

# nodeサーバ実行 (デバッグしたい場合は DEBUG*= node server 使うといいかも)
node server

2. アプリのソースを準備

https://github.com/mitolog/SIOChat
こちらにソースを上げたので、cloneして使って下さい。その際、AppConst.hファイルのkWebSocketHostNameを適宜ご自身のものに変えてください。その際、スキーマをws://にしといたほうがいいと思います。あとポート指定も。

WebSocket通信を担っている箇所

アプリ側

WebSocketの受け側は、SIOSocketのメソッドで一発です。コールバックをblocksの中で定義してあげてます。

ChatViewController.h
- (void)connectWs
{
    // Close existing connection if needed
    if(self.socket){ [self.socket close];}

    [SIOSocket socketWithHost: kWebSocketHostName response: ^(SIOSocket *socket){

        self.socket = socket;

        __weak typeof(self) weakSelf = self;
        self.socket.onConnect = ^()
        {
            /* ~ここ省略~ */
            [weakSelf.socket emit: @"init" args: @[[param paramStr]]];
        };

        [self.socket on: @"join" callback: ^(SIOParameterArray *args)
         {
             /* ~ここ省略~ */
         }];

        [self.socket on: @"update" callback: ^(SIOParameterArray *args)
         {
             /* ~ここ省略~ */
         }];

        [self.socket on: @"rooms" callback: ^(SIOParameterArray *args)
         {
             /* ~ここ省略~ */
         }];

        [self.socket on: @"disappear" callback: ^(SIOParameterArray *args)
         {                  
             /* ~ここ省略~ */
         }];
    }];
}

他のノードに通知する場合は、

ChatViewController.h
[self.socket emit:@"post" args:@[[param paramStr]]];

こんなふうにかけます。

サーバ側

これだけ!

server.js
var io = require('socket.io')(3000);

var sepStr = ',';
var roomIdx = 0;
var isParentIdx = 1;
var nicknameIdx = 3;
var joinedRooms = [];
var nicknames = [];

io.on('connection', function (socket) {

    console.log(socket['id'] + ' has connected!');

    // join the specific room everytime socket connected
    socket.on('init', function(data){

    var params = data.split(sepStr);
        var isParent = params[isParentIdx];
    var room = params[roomIdx]
        var output = data;  

    if(isParent == '1'){
            room = socket['id'];
        output = room;
            output += data;
        nicknames[socket['id']] = params[nicknameIdx];
    }else{
            socket.join(room);
    }

    joinedRooms[socket['id']] = socket.rooms;

    io.emit('rooms', io.sockets.adapter.rooms);
    io.to(room).emit('join', output);

        console.log('joined room ' + room);
    });

    // bradcast the specific room everytime someone posted some
    socket.on('post', function (data) {

        console.log(socket['id'] + ' ' + data);

        var room = data.split(sepStr)[roomIdx];
        io.to(room).emit('update', data);
    });

    // clients are disconnected automatically everytime they going background 
    socket.on('disconnect', function (data) {

    io.emit('rooms', io.sockets.adapter.rooms);

    var message = nicknames[socket['id']];
    message += ' left room';
    message += sepStr;
    var prevJoinedRooms = joinedRooms[socket['id']];
    for(var i=0; i<prevJoinedRooms.length; i++){
        var room = prevJoinedRooms[i];
        io.to(room).emit('disappear', message + room);
        console.log(message + room);
    }
        console.log(socket['id'] + ' has disconnected!');
    });

});

感想

  • アプリもサーバも、まだまだ無駄なソースあるんですが、それでも結構少ないコードですんだと思っていて、nodeすごいなーsocket.ioすごいなーと感動しました。

  • リアルタイムにチャットが飛んできた時はちょっと感動しました。

  • slackってやっぱりすげーなー...

  • socket.io-redissocket.io-emitterを使えばsocket.io以外のプロセスからsocket.ioにデータを送れるので既存のphpとかrailsのAPIからでも連携できるし、スケールアウトもできるのは良いのでは。

参考

Socket.io

SIOSocket

(公式ページ)Rooms and Namespaces

WebSocket 事始め by Node.js + Socket.IO

Node.jsにてsocket.ioのjoinでチャットルーム実装!

mito_log
ベトナム・ハノイ拠点の個人デベロッパ。東南アジア向けに素敵なサービスと文化を作るべく模索中。ベトナムで1年半農業。newbie skateboarder、ハノイITもくもく会毎週土曜朝9時頃 → https://bit.ly/2Oe9Ehk
http://mitolab.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした