<Nodejs>Websocketでシンプルなチャットアプリを作るチュートリアル

今回はNodejsのwsライブラリを使ってシンプルなチャットアプリを作ってみました。

# 作るものの詳細
・クライアントから受信したメッセージを全クライアントに配信(Websocketを使用)
・WebサーバーにはExpressを使用する
# 私の環境
・Node.js: v15.6.0
・ライブラリ: Express, WS
・OS: Windows11

# プロジェクトを作成
```:./project_root
npm init
```
Enter連打で大丈夫です。

# Webサーバーを構築する。
```js:index.js
//ここからexpress

//expressの設定
var express = require('express');
var app = express();

//expressのルーターを設定
app.get('/', (req, res) => {
    //index.htmlを返す
    res.sendFile(__dirname + '/index.html');
});
//expressのエラー処理
app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).send('Something broke!');
});

//expressのサーバー起動
app.listen(80, () => {
    console.log('Example app listening on port 80!');
});
```
expressサーバーの最小構成です。expressについて詳しく知りたい方はこちらを参考にしてください。

https://qiita.com/y-miine/items/9f5d233d3bf389cd31aa

今回は、index.htmlを返すようにしています。index.htmlの作成に付いては別のセクションで解説します。

# Websocketサーバーを構築する。
まだwsライブラリをインストールしていない場合は以下のコマンドを使用してインストールしてください。

```:./project_root
npm install ws
```
先程のindex.jsの下に、以下のコードを追加してください。
```js:index.js
//ここからws

// WebSocketのサーバの生成
let ws = require('ws')
var server = new ws.Server({port:5001});


// 接続時に呼ばれる
server.on('connection', ws => {

    //クライアンの数をカウントする
    console.log(server.clients.size + ' clients connected');

    //全クライアントに送信
    server.clients.forEach(client => {
        //入室メッセージを送信
        client.send('一人入室しました。');
        //何人オンラインかを送信
        client.send(server.clients.size + '人がオンラインです。');
    });

    // クライアントからのデータ受信時に呼ばれる
    ws.on('message', message => {

        //メッセージの内容を表示
        console.log(message.toString());

        //message.tostring()がからの場合は何もしない
        if(message.toString() == ''){
            //コンソールに表示
            console.log('メッセージが空です');
            return;
        }else{
            //メッセージを全クライアントに送信
        server.clients.forEach(client => {
            client.send(message.toString());
        });
    }
    });

    // 切断時に呼ばれる
    ws.on('close', () => {
        console.log('close');

        //クライアントの数をカウントする
        console.log(server.clients.size + ' clients connected');

        //全クライアントに送信
        server.clients.forEach(client => {
            //退室メッセージを送信
            client.send('一人退室しました。');
            //何人オンラインかを送信
            client.send(server.clients.size + '人がオンラインです。');
        });
    });

    // エラー時に呼ばれる
    ws.on('error', err => {
        console.log('error: %s', err);
    });
});
```
コメント通りなので、解説は省かさせていただきます。

# クライアントの作成

同フォルダーにindex.htmlを作成してください。
```js:index.js
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>チャットテスト</title>

    <!---cssライブラリをインストール-->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">


</head>
<body>

    <!--メッセージ-->
    <div class="form-group">
        <!-- 送信ボタン -->
        <input type="text" name="message" id="message" class="form-control">
        <input type="button" id="send_button" value="送信" class="btn btn-primary">
        <ul id="messages" class="list-group"></ul>
    </div>
    
    <script>
        // WebSocketのクライアントの生成
        let ws = new WebSocket('ws://192.168.2.100:5001');
 
        // 接続時に呼ばれる
        ws.addEventListener('open', e => {
            console.log('接続しました。')
        })

        //エラー時に呼ばれる
        ws.addEventListener('error', e => {
            console.log('エラーが発生しました。')
        })

        // Listen for messages
        ws.addEventListener('message', function (event) {
            console.log('Message from server ', event.data);

            //ulにメッセージを追加
            let messages = document.getElementById('messages')
            let li = document.createElement('li')
            li.innerText = event.data
            //liにクラスを追加
            li.className = 'list-group-item'

            messages.prepend(li)
        });

        // ボタンクリック時に呼ばれる
        document.getElementById('send_button').addEventListener('click', e => {
            //ボックスが空だったら警告を出す
            if(document.getElementById('message').value == ''){
                alert('メッセージを入力してください')
                return
            }else{
                //メッセージを送信
                ws.send(document.getElementById('message').value)
                //テキストボックスを空にする
                document.getElementById('message').value = ''
            }
        })
    </script>
```
受信したメッセージをulの一番上に追加しています。

# 完成品

<iframe width="560" height="315" src="https://www.youtube.com/embed/ziHq9dkzcBw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# 参考にした記事

・https://developer.mozilla.org/ja/docs/Web/API/WebSockets_API
・https://github.com/websockets/ws