基于Nodejs利用socket.io實(shí)現(xiàn)多人聊天室
socket.io簡介
在Html5中存在著這樣的一個(gè)新特性,引入了websocket,關(guān)于websocket的內(nèi)部實(shí)現(xiàn)原理可以看這篇文章,這篇文章講述了websocket無到有,根據(jù)協(xié)議,分析數(shù)據(jù)幀的頭,進(jìn)行構(gòu)建websocket。雖然代碼短,但可以很好地體現(xiàn)websocket的原理。
,這個(gè)特性提供了瀏覽器端和服務(wù)器端的基于TCP連接的雙向通道。但是并不是所有的瀏覽器都支持websocket特性,故為了磨平瀏覽器間的差異,為開發(fā)者提供統(tǒng)一的接口,引入了socket.io模塊。在不支持websoket的瀏覽器中,socket.io可以降級為其他的通信方式,比如有AJAX long polling ,JSONP Polling等。
模塊安裝
新建一個(gè)package.json文件,在文件中寫入如下內(nèi)容:
{
"name": "socketiochatroom",
"version": "0.0.1",
"dependencies": {
"socket.io": "*",
"express":"*"
}
}
npm install
執(zhí)行完這句,node將會從npm處下載socket.io和express模塊。
-
服務(wù)器端的實(shí)現(xiàn)
在文件夾中添加index.js文件,并在文件中寫入如下內(nèi)容:
/**
* 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 = {};
/* 在線人數(shù)*/
var onLineCounts = 0;
/*io監(jiān)聽到存在鏈接,此時(shí)回調(diào)一個(gè)socket進(jìn)行socket監(jiān)聽*/
io.on('connection', function (socket) {
console.log('a user connected');
/*監(jiān)聽新用戶加入*/
socket.on('login', function (user) {
"use strict";
//暫存socket.name 為user.userId;在用戶退出時(shí)候?qū)玫?
socket.name = user.userId;
/*不存在則加入 */
if (!onLineUsers.hasOwnProperty(user.userId)) {
//不存在則加入
onLineUsers[user.userId] = user.userName;
onLineCounts++;
}
/*一個(gè)用戶新加入,向所有客戶端監(jiān)聽login的socket的實(shí)例發(fā)送響應(yīng),響應(yīng)內(nèi)容為一個(gè)對象*/
io.emit('login', {onLineUsers: onLineUsers, onLineCounts: onLineCounts, user: user});
console.log(user.userName, "加入了聊天室");//在服務(wù)器控制臺中打印么么么用戶加入到了聊天室
});
/*監(jiān)聽用戶退出聊天室*/
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, "退出群聊");
}
})
/*監(jiān)聽到用戶發(fā)送了消息,就使用io廣播信息,信息被所有客戶端接收并顯示。注意,如果客戶端自己發(fā)送的也會接收到這個(gè)消息,故在客戶端應(yīng)當(dāng)存在這的判斷,是否收到的消息是自己發(fā)送的,故在emit時(shí),應(yīng)該將用戶的id和信息封裝成一個(gè)對象進(jìn)行廣播*/
socket.on('message', function (obj) {
"use strict";
/*監(jiān)聽到有用戶發(fā)消息,將該消息廣播給所有客戶端*/
io.emit('message', obj);
console.log(obj.userName, "說了:", obj.content);
});
});
/*監(jiān)聽3000*/
http.listen(3000, function () {
"use strict";
console.log('listening 3000');
});
運(yùn)行服務(wù)器端程序
node index.js
輸出
listening 3000
此時(shí)在瀏覽器中打開localhost:3000會得到這樣的結(jié)果:

原因是在代碼中只對路由進(jìn)行了如下設(shè)置
app.get('/', function (req, res) {
"use strict";
res.end("<h1>socket server</h1>")
});
服務(wù)器端主要是提供socketio服務(wù),并沒有設(shè)置路由。
客戶端的實(shí)現(xiàn)
在客戶端建立如下的目錄和文件,其中json3.min.js可以從網(wǎng)上下載到。
client
- - - 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="輸入聊天內(nèi)容 " 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.
*/
/*即時(shí)運(yùn)行函數(shù)*/
(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);
},
/*此處僅為簡單的刷新頁面,當(dāng)然可以做復(fù)雜點(diǎn)*/
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
};
//如在服務(wù)器端代碼所說,此對象就行想要發(fā)送的信息和發(fā)送人組合成為對象一起發(fā)送。
this.socket.emit('message', obj);
d.getElementById('content').value = '';
}
return false;
},
/**客戶端根據(jù)時(shí)間和隨機(jī)數(shù)生成ID,聊天用戶名稱可以重復(fù)*/
genUid: function () {
return new Date().getTime() + "" + Math.floor(Math.random() * 889 + 100);
},
/*更新系統(tǒng)信息
主要是在客戶端顯示當(dāng)前在線人數(shù),在線人列表等,當(dāng)有新用戶加入或者舊用戶退出群聊的時(shí)候做出頁面提示。*/
updateSysMsg: function (o, action) {
var onLineUsers = o.onLineUsers;
var onLineCounts = o.onLineCounts;
var user = o.user;
//更新在線人數(shù)
var userHtml = '';
var separator = '';
for (var key in onLineUsers) {
if (onLineUsers.hasOwnProperty(key)) {
userHtml += separator + onLineUsers[key];
separator = '、';
}
}
//插入在線人數(shù)和在線列表
d.getElementById('onLineCounts').innerHTML = '當(dāng)前共有' + onLineCounts + "在線列表: " + userHtml;
//添加系統(tǒng)消息
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設(shè)置為不顯示,將chatbox設(shè)置為顯示*/
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);//調(diào)用init方法
}
return false;
},
//用戶初始化
init: function (userName) {
//隨機(jī)數(shù)生成uid
this.userId = this.genUid();
this.userName = userName;
d.getElementById('showUserName').innerHTML = this.userName;//[newpidian]|[退出]
this.scrollToBottom();
//連接socketIO服務(wù)器,newpidian的IP地址
this.socket = io.connect('192.168.3.155:3000');
//向服務(wù)器發(fā)送某用戶已經(jīng)登錄了
this.socket.emit('login', {userId: this.userId, userName: this.userName});
//監(jiān)聽來自服務(wù)器的login,即在客戶端socket.emit('login ')發(fā)送后,客戶端就會收到來自服務(wù)器的
// io.emit('login', {onLineUsers: onLineUsers, onLineCounts: onLineCounts, user: user});
/*監(jiān)聽到有用戶login了,更新信息*/
this.socket.on('login', function (o) {
//更新系統(tǒng)信息
CHAT.updateSysMsg(o, 'login');
});
/*監(jiān)聽到有用戶logout了,更新信息*/
this.socket.on('logout', function (o) {
CHAT.updateSysMsg(o, 'logout');
});
//var obj = {
// userId: this.userId,
// userName: this.userName,
// content: content
//};
/*監(jiān)聽到有用戶發(fā)送消息了*/
this.socket.on("message", function (obj) {
//判斷消息是不是自己發(fā)送的
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();
}
};
//通過“回車鍵”提交聊天內(nèi)容
d.getElementById('content').onkeydown = function (e) {
e = e || event;
if (e.keyCode === 13) {
CHAT.submit();
}
};
})();
style.css
秘密
運(yùn)行結(jié)果
服務(wù)器端已經(jīng)運(yùn)行,現(xiàn)將客戶端也運(yùn)行起來得到下圖:

添加了new和pidian兩個(gè)用戶,并發(fā)送信息和進(jìn)行退出,得到下面的結(jié)果:

以上所述是小編給大家介紹的基于Nodejs利用socket.io實(shí)現(xiàn)多人聊天室,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
- NodeJS實(shí)現(xiàn)一個(gè)聊天室功能
- nodejs+express搭建多人聊天室步驟
- Nodejs實(shí)現(xiàn)多房間簡易聊天室功能
- Node.js websocket使用socket.io庫實(shí)現(xiàn)實(shí)時(shí)聊天室
- Node.js利用Net模塊實(shí)現(xiàn)多人命令行聊天室的方法
- Node.js中使用socket創(chuàng)建私聊和公聊聊天室
- AngularJS+Node.js實(shí)現(xiàn)在線聊天室
- 使用Angular和Nodejs、socket.io搭建聊天室及多人聊天室
- nodejs實(shí)現(xiàn)的一個(gè)簡單聊天室功能分享
- node+socket實(shí)現(xiàn)簡易聊天室功能
相關(guān)文章
輕松創(chuàng)建nodejs服務(wù)器(4):路由
這篇文章主要介紹了輕松創(chuàng)建nodejs服務(wù)器(4):路由,服務(wù)器需要根據(jù)不同的URL或請求來執(zhí)行不一樣的操作,我們可以通過路由來實(shí)現(xiàn)這個(gè)步驟,需要的朋友可以參考下2014-12-12
node實(shí)現(xiàn)socket鏈接與GPRS進(jìn)行通信的方法
這篇文章主要介紹了node實(shí)現(xiàn)socket鏈接與GPRS進(jìn)行通信的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
NodeJS開發(fā)人員常見五個(gè)錯(cuò)誤理解
這篇文章主要介紹了NodeJS開發(fā)人員常見五個(gè)錯(cuò)誤理解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
nodejs個(gè)人博客開發(fā)第五步 分配數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了nodejs個(gè)人博客開發(fā)的分配數(shù)據(jù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
node.js中的events.EventEmitter.listenerCount方法使用說明
這篇文章主要介紹了node.js中的events.EventEmitter.listenerCount方法使用說明,本文介紹了events.EventEmitter.listenerCount的方法說明、語法、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12

