Gobble up pudding

プログラミングの記事がメインのブログです。

MENU

Express 4 + EJS + Socket.IOでWebSocketをやってみる

スポンサードリンク

f:id:fa11enprince:20200416000912j:plain WebSocketをNode.js(Express 4 + EJS + Socket.IO)で復習がてら実装しました。 ハマりポイントはbin/wwwを修正しないと動いてくれないところ。
Expressってシンプルで綺麗な設計のフレームワークなんですが、
結構はまりやすいポイントが多いと思います。

Nodeおよびライブラリのバージョン

Node.js   12.14.1
Express    4.16.1
socket.io  2.3.0
EJS        2.6.0

最終成果物

次のと同じものを作る手順を記載します。

ひな形を作成

まずexpress-generatorをグローバルインストールをします。

$ npm install express-generator -g

次にひな形生成をします。いわゆるスキャフォールディングです。 プロジェクトのフォルダはexpress-ws-ejsとします。 テンプレートはEJSを選択します

$ cd express-ws-ejs
$ express --ejs

ソースコードを修正

まずsocket.ioを使えるようにします。

$ npm i socket.io --save
$ npm i

/models/chat.js

WebSocketのサーバ側です。 新規作成します。modelsフォルダを作り、chat.jsを作成します。

const socketio = require('socket.io');

function chat(server) {
    const sio = socketio.listen(server);
    sio.on('connection', function(socket) {
        socket.on('chat-message', function(msg) {
            console.log('Send message to client');
            sio.emit('chat-message', msg + '💛');
        });
        socket.on("disconnect", function() {
        });
    });
};

module.exports = chat;

■補足
いくつかあるemitの違い

const sio = socketio.listen(server);
sio.on('connection', function(socket) {
    socket.emit('chat-message', 'message'); // 送信元クライアントだけに送信
    socket.broadcast.emit('chat-message', 'message'); // 送信元を除く全クライアントに送信
    sio.emit('chat-message', 'message'); // 接続されている全クライアントに送信
}

他にも似たようなのがありますが、これだけ押さえておけばよいでしょう。
javascript - Send response to all clients except sender - Stack Overflow

/bin/www

ここでsocket.ioのlistenをしている/models/chat.jsを呼ばないといけません。
chat.jsはのちほど作成します。 app.jsでこれを呼ぶことはできません。というのもcreateServerをしているのが
/bin/wwwだからだと思われます。 ちなみにですが、varじゃなくてconstがいいのですがexpress-generatorvarで作るので、
varのままにしていたりします。

@@ -5,7 +5,8 @@
  */
 
 var app = require('../app');
-var debug = require('debug')('express-ws-ejs-o:server');
+var chat = require('../models/chat');
+var debug = require('debug')('express-ws-ejs:server');
 var http = require('http');
 
 /**
@@ -28,6 +29,7 @@
 server.listen(port);
 server.on('error', onError);
 server.on('listening', onListening);
+chat(server);
 

/app.js

usersのrouterはいらないので消します

@@ -5,7 +5,6 @@
 var logger = require('morgan');
 
 var indexRouter = require('./routes/index');
-var usersRouter = require('./routes/users');
 
 var app = express();
 
 @@ -20,7 +19,6 @@
 app.use(express.static(path.join(__dirname, 'public')));
 
 app.use('/', indexRouter);
-app.use('/users', usersRouter);
 
 // catch 404 and forward to error handler
 app.use(function(req, res, next) {

/controllers/index.js

これは新規作成します。controllersフォルダを作り、index.jsを作成します。

exports.index = function(req, res) {
    res.render('index', { title: 'Express' });
}

/routes/index.js

var express = require('express');
var router = express.Router();
var index_controller = require('../controllers/index');

router.get('/', index_controller.index);

module.exports = router;

その他リソースおよびview

主にクライアント側です。
Node使ってるとクライアント側なのかサーバ側なのかたまに混乱します。
/public/javascripts/index.js

$(function() {
    const socket = io('http://localhost:3000');
    $('form').submit(function() {
        console.log($('#m').val());
        socket.emit('chat-message', $('#m').val());
        $('#m').val('');
        return false;
    });
    socket.on('chat-message', function(msg) {
        $('#messages').append($('<li>').text(msg));
        window.scrollTo(0, document.body.scrollHeight);
    });
});

/public/stylesheets/index.css

* { 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; }
#messages { margin-bottom: 40px }

/views/index.js

@@ -2,10 +2,18 @@
 <html>
   <head>
     <title><%= title %></title>
-    <link rel='stylesheet' href='/stylesheets/style.css' />
+    <link rel='stylesheet' href='/stylesheets/index.css' />
   </head>
   <body>
-    <h1><%= title %></h1>
-    <p>Welcome to <%= title %></p>
+    <ul id="messages"></ul>
+    <form>
+        <input id="m" autocomplete="off">
+        <button type="submit">Send</button>
+    </form>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.js"></script>
+    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
+        integrity="sha256-pasqAKBDmFT4eHoN2ndd6lN370kFiGUFyTiUHWhU7k8="
+        crossorigin="anonymous"></script>
+    <script src="javascripts/index.js"></script>
   </body>
 </html>

Expressの起動

最後にExpressを起動すれば終わりです。

$ npm start

http://localhost:3000 にアクセスし、ブラウザのウィンドウを2つ以上立ち上げると動作確認ができます。 f:id:fa11enprince:20200416002210p:plain

参考

https://socket.io/get-started/chat/
https://liginc.co.jp/web/programming/node-js/132081
https://developer.mozilla.org/ja/docs/Learn/Server-side/Express_Nodejs
https://www.gitignore.io/api/node