socket.io を使用して複数人チャット ルームを実装する (Nodejs に基づく)

WBOY
リリース: 2016-06-20 13:00:57
オリジナル
1249 人が閲覧しました

socket.io を使用して複数人チャット ルームを実装します (Nodejs ベース)

socket.io の概要

WebSocket を導入する新しい機能があります。WebSocket の内部実装原理については、この記事を参照してください。この記事では、プロトコルに従って、データのヘッダーを最初から作成する方法について説明します。フレームが解析され、WebSocket が構築されます。コードは短いですが、WebSocket の原理をよく反映しています。

この機能は、ブラウザとサーバー間の TCP 接続に基づいた双方向チャネルを提供します。ただし、すべてのブラウザが WebSocket 機能をサポートしているわけではないため、ブラウザ間の差異を解消し、開発者に統一されたインターフェイスを提供するために、socket.io モジュールが導入されました。 WebSocket をサポートしていないブラウザでは、socket.io が AJAX ロング ポーリング、JSONP ポーリングなどの他の通信方法にダウングレードされる可能性があります。

モジュールのインストール

新しい package.json ファイルを作成し、ファイルに次の内容を書き込みます:

{
  "name": "socketiochatroom",
  "version": "0.0.1",
  "dependencies": {
    "socket.io": "*",
    "express":"*"
  }
}
ログイン後にコピー
npm install 
ログイン後にコピー

この文を実行すると、ノードはnpmからsocket.ioとexpressモジュールをダウンロードします。

サーバー側の実装

index.js ファイルをフォルダーに追加し、ファイルに次の内容を書き込みます:

/**
 * Created by bamboo on 2016/3/31.
 */

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function (req, res) {
    "use strict";
    res.end("<h1>socket server</h1>")
});

/*在线人员*/
var onLineUsers = {};
/* 在线人数*/
var onLineCounts = 0;

/*io监听到存在链接,此时回调一个socket进行socket监听*/
io.on('connection', function (socket) {
    console.log('a user connected');
    /*监听新用户加入*/
    socket.on('login', function (user) {
        "use strict";
        //暂存socket.name 为user.userId;在用户退出时候将会用到
        socket.name = user.userId;
        /*不存在则加入 */
        if (!onLineUsers.hasOwnProperty(user.userId)) {
            //不存在则加入
            onLineUsers[user.userId] = user.userName;
            onLineCounts++;
        }
        /*一个用户新加入,向所有客户端监听login的socket的实例发送响应,响应内容为一个对象*/
        io.emit('login', {onLineUsers: onLineUsers, onLineCounts: onLineCounts, user: user});
        console.log(user.userName, "加入了聊天室");//在服务器控制台中打印么么么用户加入到了聊天室
    });
    /*监听用户退出聊天室*/
    socket.on('disconnect', function () {
        "use strict";
        if (onLineUsers.hasOwnProperty(socket.name)) {
            var user = {userId: socket.name, userName: onLineUsers[socket.name]};
            delete onLineUsers[socket.name];
            onLineCounts--;

            /*向所有客户端广播该用户退出群聊*/
            io.emit('logout', {onLineUsers: onLineUsers, onLineCounts: onLineCounts, user: user});
            console.log(user.userName, "退出群聊");
        }
    })
    /*监听到用户发送了消息,就使用io广播信息,信息被所有客户端接收并显示。注意,如果客户端自己发送的也会接收到这个消息,故在客户端应当存在这的判断,是否收到的消息是自己发送的,故在emit时,应该将用户的id和信息封装成一个对象进行广播*/
    socket.on('message', function (obj) {
        "use strict";
        /*监听到有用户发消息,将该消息广播给所有客户端*/
        io.emit('message', obj);
        console.log(obj.userName, "说了:", obj.content);
    });
});
/*监听3000*/
http.listen(3000, function () {
    "use strict";
    console.log('listening 3000');
});
ログイン後にコピー

サーバー側プログラムを実行します

node index.js
ログイン後にコピー

出力

listening 3000
ログイン後にコピー

この時点で、ブラウザで localhost:3000 を開くと、次の結果が表示されます:

その理由は、コード内でルーティングのみが次のように設定されているためです

app.get('/', function (req, res) {
    "use strict";
    res.end("<h1>socket server</h1>")
});
ログイン後にコピー

サーバー側は主にsocketioサービスを提供しており、ルーティングの設定は行いません。

クライアント実装

クライアント上に次のディレクトリとファイルを作成します。この中の json3.min.js はインターネットからダウンロードできます。
クライアント
- - - client.js
- - -index.html
- - - json3.min.js
- - - style.css

index.html 内

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="format-detection" content="telephone=no"/>
    <meta name="format-detection" content="email=no"/>
    <title>1301群聊</title>
    <link rel="stylesheet" type="text/css" href="./style.css"/>
    <script src="http://realtime.plhwin.com:3000/socket.io/socket.io.js"></script>
    <script src="./json3.min.js"></script>
</head>
<body>
<div id="loginbox">
    <div style="width: 260px;margin: 200px auto;">
        输入你在群聊中的昵称
        <br/>
        <br/>
        <input type="text" style="width:180px;" placeholder="请输入用户名" id="userName" name="userName"/>
        <input type="button" style="width: 50px;" value="提交" onclick="CHAT.userNameSubmit();"/>
    </div>
</div>

<div id="chatbox" style="display: none;">
    <div style="background: #3d3d3d;height: 28px;width: 100%;font-size: 12px">
        <div style="line-height: 28px;color:#fff;">
            <span style="text-align: left;margin-left: 10px;">1301群聊</span>
            <span style="float: right;margin-right: 10px"><span id="showUserName"></span>|
            <a href="javascript:;" onclick="CHAT.logout()" style="color: #fff;">退出</a></span>
        </div>
    </div>
    <div id="doc">
        <div id="chat">
            <div id="message" class="message">
                <div id="onLineCounts"
                     style="background: #EFEFF4; font-size: 12px;margin-top: 10px;margin-left: 10px;color: #666;">
                </div>
            </div>
            <div class="input-box">
                <div class="input">
                    <input type="text" maxlength="140" placeholder="输入聊天内容 " id="content" name="content" >
                </div>
                <div class="action">
                    <button type="button" id="mjr_send" onclick="CHAT.submit();">提交</button>
                </div>
            </div>
        </div>
    </div>
</div>
<script type="text/javascript" src="./client.js"></script>
</body>
</html>
ログイン後にコピー

client.js 内

/**
 * Created by bamboo on 2016/3/31.
 */
 /*即时运行函数*/
(function () {
    "use strict";
    var d = document,
        w = window,
        dd = d.documentElement,
        db = d.body,
        dc = d.compatMode === "CSS1Compat",
        dx = dc ? dd : db,
        ec = encodeURIComponent,
        p = parseInt;
    w.CHAT = {
        msgObj: d.getElementById("message"),
        screenHeight: w.innerHeight ? w.innerHeight : dx.innerHeight,
        userName: null,
        userId: null,
        socket: null,
        /*滚动条始终在最底部*/
        scrollToBottom: function () {
            w.scrollTo(0, this.msgObj.clientHeight);
        },
        /*此处仅为简单的刷新页面,当然可以做复杂点*/
        logout: function () {
           // this.socket.disconnect();
            w.top.location.reload();
        },
        submit: function () {
            var content = d.getElementById('content').value;
            if (content != '') {
                var obj = {
                    userId: this.userId,
                    userName: this.userName,
                    content: content
                };
                //如在服务器端代码所说,此对象就行想要发送的信息和发送人组合成为对象一起发送。
                this.socket.emit('message', obj);
                d.getElementById('content').value = '';
            }
            return false;
        },
        /**客户端根据时间和随机数生成ID,聊天用户名称可以重复*/
        genUid: function () {
            return new Date().getTime() + "" + Math.floor(Math.random() * 889 + 100);
        },
        /*更新系统信息
        主要是在客户端显示当前在线人数,在线人列表等,当有新用户加入或者旧用户退出群聊的时候做出页面提示。*/
        updateSysMsg: function (o, action) {
            var onLineUsers = o.onLineUsers;
            var onLineCounts = o.onLineCounts;
            var user = o.user;
            //更新在线人数
            var userHtml = '';
            var separator = '';
            for (var key in onLineUsers) {
                if (onLineUsers.hasOwnProperty(key)) {
                    userHtml += separator + onLineUsers[key];
                    separator = '、';
                }
            }
            //插入在线人数和在线列表
            d.getElementById('onLineCounts').innerHTML = '当前共有' + onLineCounts + "在线列表: " + userHtml;

            //添加系统消息
            var html = '';
            html += '<div class="msg_system">';
            html += user.userName;
            html += (action === "login") ? "加入了群聊" : "退出了群聊";
            html += '</div>';
            var section = d.createElement('section');
            section.className = 'system J-mjrlinkWrap J-cutMsg';
            section.innerHTML = html;
            this.msgObj.appendChild(section);
            this.scrollToBottom();
        },

        /*用户提交用户名后,将loginbox设置为不显示,将chatbox设置为显示*/
        userNameSubmit: function () {
            var userName = d.getElementById('userName').value;
            if (userName != '') {
                d.getElementById('userName').value = '';
                d.getElementById('loginbox').style.display = 'none';
                d.getElementById('chatbox').style.display = 'block';
                this.init(userName);//调用init方法
            }
            return false;
        },
        //用户初始化
        init: function (userName) {
            //随机数生成uid
            this.userId = this.genUid();
            this.userName = userName;
            d.getElementById('showUserName').innerHTML = this.userName;//[newpidian]|[退出]
            this.scrollToBottom();
            //连接socketIO服务器,newpidian的IP地址
            this.socket = io.connect('192.168.3.155:3000');
            //向服务器发送某用户已经登录了
            this.socket.emit('login', {userId: this.userId, userName: this.userName});
            //监听来自服务器的login,即在客户端socket.emit('login ')发送后,客户端就会收到来自服务器的
            // io.emit('login', {onLineUsers: onLineUsers, onLineCounts: onLineCounts, user: user});
            /*监听到有用户login了,更新信息*/
            this.socket.on('login', function (o) {
                //更新系统信息
                CHAT.updateSysMsg(o, 'login');
            });
            /*监听到有用户logout了,更新信息*/
            this.socket.on('logout', function (o) {
                CHAT.updateSysMsg(o, 'logout');
            });
            //var obj = {
            //    userId: this.userId,
            //    userName: this.userName,
            //    content: content
            //};
            /*监听到有用户发送消息了*/
            this.socket.on("message", function (obj) {
                //判断消息是不是自己发送的
                var isMe = (obj.userId === CHAT.userId);
                var contentDiv = '<div>' + obj.content + '</div>';
                var userNameDiv = '<span>' + obj.userName + '</span>';
                var section = d.createElement('section');
                if (isMe) {
                    section.className = 'user';
                    section.innerHTML = contentDiv + userNameDiv;
                } else {
                    section.className = 'service';
                    section.innerHTML = userNameDiv + contentDiv;
                }
                CHAT.msgObj.appendChild(section);
                CHAT.scrollToBottom();
            });
        }
    }
    /*控制键键码值(keyCode)
     按键 键码  按键  键码  按键  键码  按键  键码
     BackSpace  8   Esc 27  Right Arrow 39  -_  189
     Tab    9   Spacebar    32  Dw Arrow    40  .>  190
     Clear  12  Page Up 33  Insert  45  /?  191
     Enter  13  Page Down   34  Delete  46  `~ 192
     Shift  16  End 35  Num Lock    144 [{  219
     Control    17  Home    36  ;:  186 \|  220
     Alt    18  Left Arrow  37  =+  187 ]}  221
     Cape Lock  20  Up Arrow    38  ,<  188 '"  222
     * */
    //通过“回车键”提交用户名
    d.getElementById('userName').onkeydown = function (e) {
        console.log(e);
        e = e || event;
        if (e.keyCode === 13) {
            CHAT.userNameSubmit();
        }
    };
    //通过“回车键”提交聊天内容
    d.getElementById('content').onkeydown = function (e) {
        e = e || event;
        if (e.keyCode === 13) {
            CHAT.submit();
        }
    };
})();
ログイン後にコピー


関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート