Socket.IOのドキュメントを読んで基本を勉強してみた

**この記事は私がsocket.ioのドキュメントを読んで基本を勉強したという内容ですので、間違いなどありましたらご指摘お願いします:bow:**

またドキュメントでjQueryが使われている部分はネイティブJavaScriptに書き換えています

# Get started

https://socket.io/get-started/chat/

### The web framework 【expressフレームワークを使う】

https://socket.io/get-started/chat/#The-web-framework

##### 1. Socket.IOを試すためのhtmlをexpressを使って表示

※expressではなく、普通にnodeの`http`モジュールでもOKです。

```bash:bash
yarn init -y
yarn add express@4.15.2

# または、

npm init -y
npm i -S express@4.15.2
```

##### 2. `index.js`ファイルを作成し、ブラウザで確認

```index.js
const app = require("express")();
const http = require("http").createServer(app);

app.get("/", (req, res) => {
  res.send("<h1>Hello world</h1>");
});

http.listen(3000, () => {
  console.log("listening on *:3000");
});
```

```bash
node index.js
```

http://localhost:3000/ をブラウザで開く

<img width="612" alt="Screen Shot 2019-06-02 at 12.30.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/bfecbb3f-0be9-8d2d-1046-7bb687d3358d.png">

### Serving HTML 【htmlファイルを作成】

https://socket.io/get-started/chat/#Serving-HTML

##### 1. `index.html`を作成し、その内容を表示するようにする

```index.js
const app = require("express")();
const http = require("http").createServer(app);

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

http.listen(3000, () => {
  console.log("listening on *:3000");
});
```

```index.html
<!DOCTYPE html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { font: 13px Helvetica, Arial; }
      form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
      form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
      form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages li { padding: 5px 10px; }
      #messages li:nth-child(odd) { background: #eee; }
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form action="">
      <input id="m" autocomplete="off" /><button>Send</button>
    </form>
  </body>
</html>
```

nodeから実行している`index.js`ファイルを変更した場合は<kbd>Ctrl+C</kbd>で一度サーバを切り、もう一度、`node index.js`で起動します。

<img width="612" alt="Screen Shot 2019-06-02 at 12.41.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/30d9a832-4903-c89b-03cb-cc2834395bb8.png">

### Integrating Socket.IO 【Nodeサーバにsocket.ioを統合】

https://socket.io/get-started/chat/#Integrating-Socket-IO

##### `socket.io`は2つの部分から構成されている

- Nodeのサーバで動く機能
- ブラウザで動く機能

```bash:bash
# socket.ioをインストールすると、↑の2つの機能はどちらも使えるようになります

yarn add socket.io
# または、
npm i -S socket.io
```

##### 1. `index.js`で`socket.io`の接続イベントを設定

```index.js
const app = require("express")();
const http = require("http").Server(app);
const io = require("socket.io")(http); // Nodeのサーバで動く機能

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

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

http.listen(3000, () => {
  console.log("listening on *:3000");
});
```

##### 2. `index.html`を変更

```index.html
<body>
<ul id="messages"></ul>
<form action="">
    <input id="m" autocomplete="off" /><button>Send</button>
</form>

<!-- ブラウザで動く機能 -->
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io();
</script>
</body>
```

この状態で、nodeサーバ再起動、ブラウザリロードすると、接続されたことが確認できます

![Screen Shot 2019-06-02 at 17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/40b7b07b-1664-43a5-1e48-b9c601b72c8a.png)


##### 3. 切断イベントを設定

```index.js
io.on("connection", socket => {
  console.log("a user connected");
  socket.on("disconnect", () => {
    console.log("user disconnected");
  });
});
```

ブラウザのタブを閉じるなどすると、切断されて、`user disconnected`とログに表示されます

### Emitting events 【イベント発行】

https://socket.io/get-started/chat/#Emitting-events

```index.html
<body>
<ul id="messages"></ul>
<form action="">
    <input id="m" autocomplete="off" /><button>Send</button>
</form>

<script src="/socket.io/socket.io.js"></script>
<script>
{
    const socket = io();
    document.querySelector("form").addEventListener("submit", e => {
        e.preventDefault();
        socket.emit('chat message', document.querySelector("#m").value);
        document.querySelector("#m").value = "";
    });
}
</script>
</body>
```

```index.js
io.on("connection", socket => {
  socket.on("chat message", msg => {
    console.log("message: " + msg);
  });
});
```

![DbohIXQoRB.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/78987e48-7019-6bcc-eef2-4c9b7c490013.gif)

イベントを設定して入力値を送信することができました

### Broadcasting 【他のユーザーにイベント送信】

https://socket.io/get-started/chat/#Broadcasting

##### `io.emit('some event', { for: 'everyone' });`

```index.js
io.on("connection", socket => {
  socket.on("chat message", msg => {
    io.emit("chat message", msg); // ブラウザに送信
  });
});
```

```index.html
<body>
<ul id="messages"></ul>
<form action="">
    <input id="m" autocomplete="off" /><button>Send</button>
</form>

<script src="/socket.io/socket.io.js"></script>
<script>
{
    const socket = io();
    document.querySelector("form").addEventListener("submit", e => {
        e.preventDefault();
        socket.emit('chat message', document.querySelector("#m").value);
        document.querySelector("#m").value = "";
    });
    socket.on('chat message', msg => {
        // イベントを受信してhtmlに表示
        document.querySelector("#messages").insertAdjacentHTML("beforeend", `
            <li>${msg}</li>
        `);
    });
}
</script>
</body>
```

![QMyKqPlZ7A.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/57e6f316-b239-7d73-f2f0-9c2d846a3e92.gif)

接続しているユーザーに送信することができました。

# Overview

https://socket.io/docs/

## What Socket.IO is

https://socket.io/docs/#What-Socket-IO-is

google翻訳↓

> Socket.IOは、ブラウザとサーバー間で双方向の双方向のリアルタイム通信を可能にするライブラリです。

`ブラウザ->サーバー`、`サーバー->ブラウザ`でデータ送信できます。

## Using with Node http server 【Nodeのhttpモジュールで使う】

https://socket.io/docs/#Using-with-Node-http-server

### Server (index.js)

```index.js
const app = require("http").createServer(handler);
const io = require("socket.io")(app);
const fs = require("fs");

app.listen(3000);

function handler(req, res) {
  fs.readFile(__dirname + "/index.html", (err, data) => {
    if (err) {
      res.writeHead(500);
      return res.end("Error loading index.html");
    }

    res.writeHead(200);
    res.end(data);
  });
}

io.on("connection", socket => {
  socket.emit("news", { hello: "world" }); // 接続してすぐに`news`というイベント名でブラウザにデータ送信
  socket.on("my other event", data => { // `my other event`でイベントを受け取る
    console.log(data);
  });
});
```

### Client (index.html)

```index.html
<script src="/socket.io/socket.io.js"></script>
<script>
{
    const socket = io();
    socket.on("news", msg => { // newsイベントのデータを受け取る
        console.log("news")
    });
    socket.emit("my other event", { my: 'data' }); // `my other event`というイベント名でサーバにデータ送信
}
</script>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/4580dbd1-4a40-e71d-8918-889507305b20.png)

## Using with Express 【Expressで使う】

https://socket.io/docs/#Using-with-Express

### Server (index.js)

```index.js
const app = require("express")();
const server = require("http").Server(app);
const io = require("socket.io")(server);

server.listen(3000);

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

io.on("connection", socket => {
  socket.emit("news", { hello: "world" }); // 接続してすぐに`news`というイベント名でブラウザにデータ送信
  socket.on("my other event", data => { // `my other event`でイベントを受け取る
    console.log(data);
  });
});
```

### Client (index.html)

```index.html
<script src="/socket.io/socket.io.js"></script>
<script>
{
    const socket = io();
    socket.on("news", msg => { // newsイベントのデータを受け取る
        console.log(msg)
    });
    socket.emit("my other event", { my: 'data' }); // `my other event`というイベント名でサーバにデータ送信
}
</script>
```

## Restricting yourself to a namespace 【名前空間を設定できる】

https://socket.io/docs/#Restricting-yourself-to-a-namespace

### Server (index.js)

##### `server.of('/hoge')`

イベント名が同じでも、名前空間で設定したイベントを実行してくれます。

```index.js
const app = require("express")();
const server = require("http").Server(app);
const io = require("socket.io")(server);

server.listen(3000);

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

// http:localhost:3000/chatで名前空間を設定
const chat = io.of("/chat").on("connection", socket => {
  socket.emit("a message", {
    that: "only",
    "/chat": "will get"
  });
  chat.emit("a message", {
    everyone: "in",
    "/chat": "will get"
  });
});

// http:localhost:3000/newsで名前空間を設定
const news = io.of("/news").on("connection", socket => {
  socket.emit("item", { news: "item" });
  news.emit("a message", {
    everyone: "in",
    "/news": "will get"
  });
});
```

### Client (index.html)

```index.html
<script src="/socket.io/socket.io.js"></script>
<script>
    const chat = io.connect('http://localhost:3000/chat')
        , news = io.connect('http://localhost:3000/news');
    
    chat.on('a message', data => {
        console.log(data);
    });
    news.on('item', data => {
        console.log(data);
    });
    news.on('a message', data => {
        console.log(data);
    });
</script>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/b7f63619-6d88-0597-e045-f061d8b10e7a.png)

## Sending and getting data (acknowledgements) 【データ送信】

https://socket.io/docs/#Sending-and-getting-data-acknowledgements

### Server (index.js)

```index.js
const app = require("express")();
const server = require("http").Server(app);
const io = require("socket.io")(server);

server.listen(3000);

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

io.on("connection", socket => {
  socket.on("ferret", (name, word, fn) => {
    fn(`${name} says ${word}`);
  });
});
```

### Client (index.html)

```index.html
<script src="/socket.io/socket.io.js"></script>
<script>
    const socket = io();
    
    socket.on("connect", () => {
        socket.emit("ferret", "tobi", "woot", data => {
            console.log(data);
        });
    });
</script>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/ded019ef-adba-dd94-3799-b0f890a4fdac.png)

## Broadcasting messages 【自分以外にデータ送信】

https://socket.io/docs/#Broadcasting-messages

### Server

##### `broadcast`

先ほどから使っていた`emit`の前に`broadcast`とつけると自分以外にデータ送信してくれます

```index.js
const app = require("express")();
const server = require("http").Server(app);
const io = require("socket.io")(server);

server.listen(3000);

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

io.on("connection", socket => {
  socket.broadcast.emit("user connected");
});
```

```index.html
<script src="/socket.io/socket.io.js"></script>
<script>
    const socket = io();
    
    socket.on("connect", () => {
        socket.on("user connected", () => {
            console.log("connected");
        });
    });
</script>
```

![1rvVeYV8eI.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/de09693f-b056-e5ee-d701-7ce9dd043dfe.gif)

# Rooms and Namespaces

## Namespaces

https://socket.io/docs/rooms-and-namespaces/#Namespaces

google翻訳↓

> Socket.IOを使用すると、ソケットに「名前を付ける」ことができます。これは基本的に、異なるエンドポイントまたはパスを割り当てることを意味します。

### Custom namespaces

```index.js
const app = require("express")();
const server = require("http").Server(app);
const io = require("socket.io")(server);

server.listen(3000);

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

const nsp = io.of("/my-namespace");
nsp.on("connection", function(socket) {
  console.log("someone connected");
});
```

```index.html
<script src="/socket.io/socket.io.js"></script>
<script>
    const myNamespace = io("/my-namespace");
</script>
```

![Screen Shot 2019-06-02 at 17.55.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/d134fd66-fa96-db41-10b4-ecc79331b984.png)

## Rooms

https://socket.io/docs/rooms-and-namespaces/#Rooms

google翻訳↓

> 各名前空間内で、ソケットが**参加**および**脱退**できる任意のチャネルを定義することもできます。

### Joining and leaving

##### `join`

`join`で特定のチャンネルに登録する

```index.js
const app = require("express")();
const server = require("http").Server(app);
const io = require("socket.io")(server);

server.listen(3000);

app.get("/", (req, res) => {
  res.sendFile(__dirname + "/index.html");
});

io.on("connection", socket => {
  socket.on("chat message", ({ msg, room }) => {
    socket.join(room); // 指定のチャンネルに追加
    io.to(room).emit("chat message", msg);
  });
});
```

```index.html
<body>
<ul id="messages"></ul>
<form action="">
    <input type="radio" name="room" value="1" checked>room1
    <input type="radio" name="room" value="2">room2
    <br>
    <input id="m" autocomplete="off" /><button>Send</button>
</form>

<script src="/socket.io/socket.io.js"></script>
<script>
    const socket = io();
    document.querySelector("form").addEventListener("submit", e => {
        e.preventDefault();
        socket.emit('chat message', {
            msg: document.querySelector("#m").value,
            room: `room-${e.target.room.value}`,
        });
    });
    socket.on('chat message', msg => {
        document.querySelector("#messages").insertAdjacentHTML("beforeend", `
            <li>${msg}</li>
        `);
    });
</script>
</body>
```

![qodIGnFTba.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/fd09bad5-692c-c96a-ba0a-f5fd9dfc8f2c.gif)

特定のルームの人にだけデータ送信できました。
※チャンネルを離れるには、`join`と同じ方法で`leave`を呼び出せば良いということでした。

---

最後まで読んでいただいてありがとうございましたm(_ _)m