Socket.io 를 이용한 채팅서버 구축하기

By | 2020년 8월 13일
Table of Contents

Socket.io 를 이용한 채팅서버 구축하기

Socket.io 설치하기

Local 계정에 설치해야 정상적으로 작동합니다.

여러 인스턴스간에 데이타를 공유하기 위해 Redis 서버를 설치합니다.

sudo apt-get install redis-server
npm install node-gyp
npm install socket.io
npm install --save-optional bufferutil utf-8-validate
npm install express
npm install socket.io-redis

서버 생성

server.js

mkdir chat
vi chat/server.js
var express = require('express');
var app = express();
var redisAdapter = require('socket.io-redis');
var server = require('http').Server(app);
var io = require('socket.io')(server);
var redis = require('redis');
//var Promise = require('promise');
const path = require('path');

var favicon = require('serve-favicon');
app.use(favicon(path.join(__dirname, 'public/images', 'favicon.ico')));

io.eio.pingTimeout = 60*60*1000; // 60 minutes
io.eio.pingInterval = 5*1000;  // 5 seconds

server.listen(3000, () => {
    console.log('server on!');
});

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

const namespace1 = io.of('/namespace1');

// 다중 인스턴스간 데이타 공유
io.adapter(redisAdapter({ host: 'localhost', port: 6379 }));

// redis 클라이언트 생성
const REDIS_PREFIX = 'CHAT_USER_';
const client = redis.createClient();

client.on('error', function (err) {
    console.log('Error ' + err);
});

namespace1.on('connection', (socket) =>  {
    socket.nickname = socket.id;
    socket.emit('new',{ nickname : socket.nickname });
    console.log('user connected : ', socket.nickname);

    client.hmset(
        REDIS_PREFIX + socket.id,
        'nickname', socket.nickname
    );

    // 채팅방 접속
    socket.join('my room');

    // 접속 종료
    socket.on('disconnect', function(){
        console.log('user disconnected: ', socket.id);
        client.hdel(
            REDIS_PREFIX + socket.id,
            'nickname'
        );
    });

    // 서버 메시지 수신
    socket.on('send message', function(name,text){
        var msg = 'send message ' + name + ' : ' + text;
        namespace1.to('my room').emit('receive message', msg);
    });

    // 대화방 메시지 수신
    socket.on('chat message', function(name,text){
        var msg = 'chat message ' + name + ' : ' + text;
        namespace1.to('my room').emit('chat message', msg);
    });

    function saveNickname(socketId, nickname) {
        return new Promise(function(resolved, rejected) {
            client.hmset(
                REDIS_PREFIX + socketId,
                'nickname', nickname
            );
            return resolved(nickname);
        });
    };

    function sendToUser(nickname) {
        return new Promise(function(resolved, rejected) {
            sendTo('aaa', 'new nickname : ' + nickname);
            return resolved('ok');
        });
    };

    // 닉네임 변경
    socket.on('changename', function(data) {
        socket.nickname = data.nickname;

        (async() => {
            const nickname = await saveNickname(socket.id, socket.nickname);
            await sendToUser(nickname);
        })();
    });

    function sendTo(nickname, msg) {
        var socketId, clientId, i;

        io.of('/namespace1').adapter.clients(['my room'], (err, clients) => {
            if (err) {
                console.log(err);
                return;
            }
            for (i = 0; i < clients.length; i++) {
                socketId = clients[i];

                // 클로저 이용
                (function(socketId) {
                    client.hgetall(REDIS_PREFIX + socketId, (err, _socket) => {
                        if (_socket.hasOwnProperty('nickname') && _socket.nickname == nickname) {
                            namespace1.to(socketId).emit('send message to', { msg: msg });
                        }
                    });
                })(socketId);
            }
        });
    }
});

client.html

vi chat/client.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Chat</title>
    <style>
      .chat_log{ width: 95%; height: 200px; }
      .name{ width: 10%; }
      .message{ width: 70%; }
      .chat{ width: 10%; }
    </style>
  </head>
  <body>
    <div>
      <textarea id="chatLog" class="chat_log" readonly></textarea>
    </div>
    <form id="chat">
      <input id="nickname" class="name" type="text" />
      <input id="changename" type="button" class="chat" value="changename"/><br />
      <input id="message" class="message" type="text" autocomplete="off" />
      <input type="submit" class="chat" value="chat"/>
    </form>
    <script src="/socket.io/socket.io.js"></script>
    <script src="http://code.jquery.com/jquery-1.11.1.js"></script>
    <script type="text/javascript">
    // 접속
    var socket = io('/namespace1', {transports: ['websocket']});

    // 메시지 전송
    $('#chat').on('submit', function(e){
        // send message : 서버 메시지 전송
        // chat message : 대화방 메시지 전송
        // socket.emit('send message', $('#nickname').val(), $('#message').val());
        socket.emit('chat message', $('#nickname').val(), $('#message').val());
        $('#message').val("");
        $("#message").focus();
        e.preventDefault();
    });

    // 서버 메시지 수신
    socket.on('receive message', function(msg){
        $('#chatLog').append(msg+'\n');
        $('#chatLog').scrollTop($('#chatLog')[0].scrollHeight);
    });

    // 대화방 메시지 수신
    socket.on('chat message', function(msg){
        $('#chatLog').append(msg+'\n');
        $('#chatLog').scrollTop($('#chatLog')[0].scrollHeight);
    });

    // 지정 메시지 수신
    socket.on('send message to',function(data){
        console.log(data.msg);
    });

    // 닉네임 수신
    socket.on('new',function(data){
        $('#nickname').val(data.nickname);
    });

    // 닉네임 변경
    $('#changename').on('click', function(e){
        socket.emit('changename', { nickname : $('#nickname').val() });
    });
    </script>
  </body>
</html>

서버 실행

cd chat
vi ecosystem.config.js
module.exports = {
  apps : [{
    name       : 'chat-server',
    script     : 'server.js',
    watch      : '.',
    instances  : 4
  }]};
pm2 start ecosystem.config.js
pm2 log
pm2 list
pm2 save

브라우저로 접속하면 정상적으로 서버가 실행되는 것을 확인할 수 있습니다.

http://XXX.XXX.XXX.102:3000/

두개 이상의 브라우저를 열어 채팅이 공유되는 것을 확인 할 수 있습니다.

One thought on “Socket.io 를 이용한 채팅서버 구축하기

  1. Pingback: My Share Link – DEBTOLEE

답글 남기기