Socket.IO を触ってみた

## Socket.IO とは

https://socket.io/

WebSocket 通信を実装するための JavaScript ライブラリです
WebSocket とはなんぞや、という記事は [こちら](https://qiita.com/y-miine/items/1482d86bc34fe78f0915)

[公式のチュートリアル](https://socket.io/get-started/chat/) に沿って「チャットアプリ」を作っていきます
細かいことはチュートリアルを見ればわかるので、ポイントだけ

## コネクションの確立

### サーバ側
```javascript:index.js
var app = require('express')();
var http = require('http').createServer(app);
// httpサーバーオブジェクトを渡して Socket.IO のインスタンスを初期化する
var io = require('socket.io')(http); 

// 中略

// コネクションが確立すると呼ばれる
io.on('connection', (socket) => {
  console.log('a user connected');
});
```

### クライアント側

jQuery はサンプルコードで使用するので読み込んでます

```html:index.html
<!-- いろいろ省略 -->
<html>
  <body>
    <!-- クライアント用の js を読み込む（これはサーバからGETしてるだけ） -->
    <script src="/socket.io/socket.io.js"></script>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
      // インスタンスを初期化、これで接続される
      var socket = io();
    </script>
  </body>
</html>
```

## クライアント => サーバ

### クライアント側

`emit('イベント名', {データ})` でサーバにイベントを通知します

```html:index.html
<html>
  <body>
    <script src="/socket.io/socket.io.js"></script>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script>
      var socket = io();

      // チャットメッセージの送信
      $('form').submit(function(e) {
          e.preventDefault();
          var msg = $('#m').val()
          socket.emit('chat message', msg); // サーバに伝達
          $('#m').val('');
          return false;
      });
    </script>
  </body>
</html>
```

### サーバ側

`socket.on('イベント名', {コールバック関数})` でイベントを受け取ります

```javascript:index.js
// 中略

io.on('connection', (socket) => {
  console.log('a user connected');

  socket.on('chat message', (msg) => {
    // なんらかの処理
  });
});
```

## サーバ => クライアント

### サーバ側

さきほどのクライアント側と同じく `emit('イベント名', {データ})` で送ります

```javascript:index.js
// 中略

io.on('connection', (socket) => {
  console.log('a user connected');

  socket.on('chat message', (msg) => {
    // io.emit => 送信者含む全員に送る場合
    io.emit('chat message', msg);

    // socket.broadcast.emit => 送信者を除く全員に送る場合
    socket.broadcast.emit('chat message', msg);
  });
});
```

### クライアント側

さきほどサーバ側と同じく `socket.on('イベント名', {コールバック関数})` でイベントを受け取ります

```html:index.html
<html>
  <body>
    <script src="/socket.io/socket.io.js"></script>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script>
      var socket = io();
      // 中略
      socket.on('chat message', function(msg){
        // なんらかの処理
      });
    </script>
  </body>
</html>
```

手っ取り早く動きを確認してみたい場合は、チャットアプリのサンプルが公式で提供されているので clone してすぐに動かすことができます
https://github.com/socketio/chat-example

## Homework

チュートリアルで Homework なるより学習を深めるためのお題が公開されているのでやってみました
コードは [こちら](https://github.com/y-miine/socketio-sample) に置いてます

意訳も入っていますが、課題はこの6つです

- ユーザーが接続/切断した際にメッセージを送信する
- ニックネーム機能を追加する
- 送信者自身にはサーバを介してメッセージを送信せず、直接メッセージを追加する
- タイピング中の人を表示する
- オンラインの人を表示する
- プライベートメッセージ機能を追加する

個人的にポイントに感じたことだけ書きます（笑）

### オンラインの人を表示する

オンラインのユーザを管理する配列を用意して、入退室時に配列に格納/削除する、というところまでは普通です
`socket.id` で接続中のユーザのIDが取得できるのでこれを利用します

```javascript:index.js
// 中略

var onlineUsers = []

io.on('connection', (socket) => {

  // 退室
  socket.on('disconnect', () => {
    var index = onlineUsers.findIndex(item => item.id === socket.id);
    onlineUsers.splice(index, 1);
    io.emit('user update', onlineUsers);
  });

  // 入室
  socket.on('enter room', (nickname) => {
    onlineUsers.push({ id: socket.id, nickname: nickname });
    io.emit('user update', onlineUsers);
  });
```

#### 詰まったところ

確認するときに、ブラウザのタブを複数開いて「リロード」していたのですが、なにやらうまくいかないぞ...
結論としては、リロードすると一度 `disconnect` イベントが発生してしまうので、それが原因のようでした
確認の際は、面倒ですが一度タブを閉じ、再度ページにアクセスするとよいと思います

### プライベートメッセージ機能を追加する

クライアント側の実装などが面倒そうだったのでちゃんと作ってはないです（笑）

`io.to({ソケットID}).emit('イベント名', {データ})` で特定の人にだけ送れます


## 最後に

チュートリアルを通して Socket.IO の基本的な使い方を勉強してみました
room 機能などもあるみたいなのでまたいつか使ってみたいです