目錄
socket.io简介
socket server
运行服务器端程序
在index.html中
在client.js中
 
首頁 後端開發 php教程 利用socket.io实现多人聊天室(基于Nodejs)

利用socket.io实现多人聊天室(基于Nodejs)

Jun 20, 2016 pm 01:00 PM
Socket

利用socket.io实现多人聊天室(基于Nodejs)

socket.io简介

在Html5中存在着这样的一个新特性,引入了websocket,关于websocket的内部实现原理可以看这篇文章,这篇文章讲述了websocket无到有,根据协议,分析数据帧的头,进行构建websocket。虽然代码短,但可以很好地体现websocket的原理。

这个特性提供了浏览器端和服务器端的基于TCP连接的双向通道。但是并不是所有的浏览器都支持websocket特性,故为了磨平浏览器间的差异,为开发者提供统一的接口,引入了socket.io模块。在不支持websoket的浏览器中,socket.io可以降级为其他的通信方式,比如有AJAX long polling ,JSONP Polling等。

模块安装

新建一个package.json文件,在文件中写入如下内容:

{
  "name": "socketiochatroom",
  "version": "0.0.1",
  "dependencies": {
    "socket.io": "*",
    "express":"*"
  }
}
登入後複製
npm install 
登入後複製

执行完这句,node将会从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 id="socket-server">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 id="socket-server">socket server</h1>")
});
登入後複製

服务器端主要是提供socketio服务,并没有设置路由。

客户端的实现

在客户端建立如下的目录和文件,其中json3.min.js可以从网上下载到。
client
- - - client.js
- - - index.html
- - - json3.min.js
- - - style.css

在index.html中



    <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>


<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>

登入後複製

在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  ,

<h2 id=""> </h2>

<p> </p><p class="item-note"><br></p>
登入後複製
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1670
14
CakePHP 教程
1428
52
Laravel 教程
1329
25
PHP教程
1274
29
C# 教程
1256
24
說明PHP中的安全密碼散列(例如,password_hash,password_verify)。為什麼不使用MD5或SHA1? 說明PHP中的安全密碼散列(例如,password_hash,password_verify)。為什麼不使用MD5或SHA1? Apr 17, 2025 am 12:06 AM

在PHP中,應使用password_hash和password_verify函數實現安全的密碼哈希處理,不應使用MD5或SHA1。1)password_hash生成包含鹽值的哈希,增強安全性。 2)password_verify驗證密碼,通過比較哈希值確保安全。 3)MD5和SHA1易受攻擊且缺乏鹽值,不適合現代密碼安全。

PHP和Python:比較兩種流行的編程語言 PHP和Python:比較兩種流行的編程語言 Apr 14, 2025 am 12:13 AM

PHP和Python各有優勢,選擇依據項目需求。 1.PHP適合web開發,尤其快速開發和維護網站。 2.Python適用於數據科學、機器學習和人工智能,語法簡潔,適合初學者。

PHP行動:現實世界中的示例和應用程序 PHP行動:現實世界中的示例和應用程序 Apr 14, 2025 am 12:19 AM

PHP在電子商務、內容管理系統和API開發中廣泛應用。 1)電子商務:用於購物車功能和支付處理。 2)內容管理系統:用於動態內容生成和用戶管理。 3)API開發:用於RESTfulAPI開發和API安全性。通過性能優化和最佳實踐,PHP應用的效率和可維護性得以提升。

PHP類型提示如何起作用,包括標量類型,返回類型,聯合類型和無效類型? PHP類型提示如何起作用,包括標量類型,返回類型,聯合類型和無效類型? Apr 17, 2025 am 12:25 AM

PHP類型提示提升代碼質量和可讀性。 1)標量類型提示:自PHP7.0起,允許在函數參數中指定基本數據類型,如int、float等。 2)返回類型提示:確保函數返回值類型的一致性。 3)聯合類型提示:自PHP8.0起,允許在函數參數或返回值中指定多個類型。 4)可空類型提示:允許包含null值,處理可能返回空值的函數。

PHP的持久相關性:它還活著嗎? PHP的持久相關性:它還活著嗎? Apr 14, 2025 am 12:12 AM

PHP仍然具有活力,其在現代編程領域中依然佔據重要地位。 1)PHP的簡單易學和強大社區支持使其在Web開發中廣泛應用;2)其靈活性和穩定性使其在處理Web表單、數據庫操作和文件處理等方面表現出色;3)PHP不斷進化和優化,適用於初學者和經驗豐富的開發者。

PHP和Python:解釋了不同的範例 PHP和Python:解釋了不同的範例 Apr 18, 2025 am 12:26 AM

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。

PHP和Python:代碼示例和比較 PHP和Python:代碼示例和比較 Apr 15, 2025 am 12:07 AM

PHP和Python各有優劣,選擇取決於項目需求和個人偏好。 1.PHP適合快速開發和維護大型Web應用。 2.Python在數據科學和機器學習領域佔據主導地位。

您如何防止PHP中的SQL注入? (準備的陳述,PDO) 您如何防止PHP中的SQL注入? (準備的陳述,PDO) Apr 15, 2025 am 12:15 AM

在PHP中使用預處理語句和PDO可以有效防範SQL注入攻擊。 1)使用PDO連接數據庫並設置錯誤模式。 2)通過prepare方法創建預處理語句,使用佔位符和execute方法傳遞數據。 3)處理查詢結果並確保代碼的安全性和性能。

See all articles