Day12
今日份内容:使用WebSocket制作基于Node.js的网络聊天室
WebSocket是一种基于TCP协议的通信技术,与传统HTTP有一定区别。
WebSocket
- 了解WebSocket通信机制
- 熟悉WebSocket API接口
- 使用WebSocket技术实现双向通信
- 了解Notification消息提醒机制
内容
- 配置Node.js环境
- 项目初始化
- 安装Express、WebSocket、node_uuid框架
- 服务端实现
- 客户端实现
代码
// index.js
var express = require('express');
const http = require('http');
const url = require('url');
const WebSocket = require('ws');
const uuid = require('node-uuid');
var app = express();
//下面会修改临时文件的存储位置
app.use(express.static('public'));
//设置http服务监听的端口号
app.set('port', process.env.PORT || 3000);
app.listen(app.get('port'), function() {
console.log("Express started on localhost:" + app.get('port') + ':press Ctrl-C to terminate.');
});
//浏览器访问localhost会输出一个html文件
app.get('/', function(req, res) {
res.type('text/html');
res.sendFile('./index.html');
});
const server = http.createServer(app);
const wss = new WebSocket.Server({
server
});
server.listen(8080, function listening() {
console.log('Listening on %d', server.address().port);
});
var connections = [];
var userList = [];
var chatList = [];
var headImages = ["head1", "head2", "head3", "head4", "head5"];
wss.on('connection', function connection(ws, req) {
var currentUser;
var client_uuid = uuid.v4();
connections.push({
"id": client_uuid,
"ws": ws
});
console.log('&&&&&client[%s]连接成功!&&&&&', client_uuid);
//用户连接时,获得在线用户列表
ws.send(getUsersOnLine());
ws.on('message', function incoming(message) {
let json = JSON.parse(message);
//当用户发出登录请求时
if (json.op == "login") {
let userName = json.userName;
let loginTime = new Date();
let index = Math.ceil(Math.random() * (headImages.length));
//封装登录用户信息,以便保存到WebSocket中
currentUser = {
'userName': userName,
'loginTime': loginTime,
'headImages': headImages[index]
};
userList.push({
"id": client_uuid,
"user": currentUser
});
console.log('########client[%s]登录成功########', currentUser);
broadCast(getUsersOnLine());
broadCastNotification(getNewUser(currentUser.userName), ws);
//发送用户登录会话
ws.send(getUserWithConnectionInfo(client_uuid));
} else if (json.op = "chat") {
let connectionId = json.connectionId;
let chatMessage = json.chatMessage;
let time = json.time;
let user = getUserFromUserList(connectionId);
let msg = {
'user': user,
'charMessage': charMessage,
'time': time
};
chatList.push({
"id": client_uuid,
"msg": msg
});
broadCast(getAllMessage());
}
});
ws.on('close', function() {
for (var i in connections) {
if (connections[i].ws == ws) {
connections.pop(ws);
break;
}
}
if (currentUser != null && currentUser.userName != null) {
userList.pop(currentUser);
broadCastNotification(getUserOffLine(currentUser.userName), ws);
broadCast(getUsersOnLine());
}
ws.close();
});
});
function onLineList(message, ws) {
ws.send(message);
}
//向客户端以数组形式发送在线用户列表
function broadCast(message) {
for (var i in connections) {
try {
if (connections[i].ws.readyState === WebSocket.OPEN) {
//参数为字符串类型
connections[i].ws.send(Message);
}
} catch (e) {
console.log(e);
connections.pop(connections[i]);
}
}
}
//广播通知消息
function broadCastNotification(message, ws) {
for (var i in connections) {
try {
if (connections[i].ws.readyState === WebSocket.OPEN && connections[i].ws != ws) {
//参数为字符串类型
connections[i].ws.send(message);
}
} catch (e) {
//TODO handle the exception
console.log(e);
connections.pop(connections[i]);
}
}
}
//返回现在用户列表,并以字符串形式返回
function getUsersOnLine() {
let jsonObject = {};
jsonObject.users = userList;
jsonObject.contentType = 'userOnLineList';
return JSON.stringify(jsonObject);
}
//获得新用户上线信息,并以字符串形式返回
function getNewUser(userName) {
let jsonObject = {};
jsonObject.userName = userName;
jsonObject.contentType = 'newUserOnLine';
return JSON.stringify(jsonObject);
}
//封装离散用户
function getUserOffLine(userName) {
let jsonObject = {};
jsonObject.userName = userName;
jsonObject.contentType = 'userOffLine';
return JSON.stringify(jsonObject);
}
//返回指定的客户信息
function getUserWithConnectionInfo(connectionId) {
let jsonObject = {};
jsonObject.connectionId = connectionId;
jsonObject.contentType = "userSession";
return JSON.stringify(jsonObject);
}
//根据connectionId查找在线用户列表
function getUserFromUserList(connectionId) {
for (var i in userList) {
if (userList[i].id == connectionId) {
return userList[i].user;
}
}
return null;
}
//返回所有的聊天信息
function getAllMessage() {
let jsonObject = {};
jsonObject.messages = chatList;
jsonObject.contentType = 'chatMessages';
return JSON.stringify(jsonObject);
}
// clinetWebSocket.js
var serverAddress = document.getElementById("serverAdress").value;
var serverPort = document.getElementById("serverPort").value;
var url = "ws://" + serverAddress + ":" + serverPort + "/ChatRoom_modify/chatRoomWebsocket";
var webSocket = new WebSocket(url);
var userName;
//用户注册
function loginUser() {
userName = document.getElementById("userName").value;
var user = new User("login", userName);
//界面模块切换
document.getElementById("chatPart").style.display = "block";
document.getElementById("configure").style.display = "none";
document.getElementById("messages").style.display = "block";
document.getElementById("connectStatus").style.display = "none";
document.getElementById("serverAdressTxt").innerHTML = serverAddress;
document.getElementById("serverPortTxt").innerHTML = serverPort;
document.getElementById("userNameTxt").innerHTML = userName;
//发送注册信息
webSocket.send(JSON.stringify(user));
}
//发送聊天信息
function sendMessage() {
var chatMessage = document.getElementById("chatMessage");
var connectionId = document.getElementById("connectionId").value;
var message = new Message('chat', connectionId, chatMessage.value, getTime());
chatMessage.value = '';
webSocket.send(JSON.stringify(message));
}
function User(op, userName) {
this.op = op;
this.userName = userName;
}
function Message(op, connectionId, chatMessage, time) {
this.op = op;
this.connectionId = connectionId;
this.chatMessage = chatMessage;
this.time = time;
}
function getTime() {
var now = new Date();
return now.getFullYear() + "/" + (now.getMonth() + 1) + "/" + now.getDate() +
"" + now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds();
}
webSocket.onerror = function(event) {
alert(event.data);
};
//与WebSocket建立连接
webSocket.onopen = function(event) {
document.getElementById('connectDiv').className = "online-img";
};
//处理服务器返回的信息
webSocket.onmessage = function(event) {
var userList = "";
var messageList = "";
var returnContent = eval("(" + event.data + ")");
console.log(returnContent);
if (returnContent.contentType == "userSession") {
document.getElementById('connectionId').value = returnContent.connectionId;
}
if (returnContent.contentType == "userOnLineList") {
var users = returnContent.users;
for (var i = 0; i < users.length; i++) {
userList += '<li><span class="' + users[i].user.headImages + '"></span>' + user[i].user.userName +
'</li>';
}
document.getElementById('userOnLine').innerHTML = userList;
document.getElementById('userList').className = "none-img none";
}
if (returnContent.contentType == "newUserOnLine") {
var userName = returnContent.userName;
NotificationHandler.showNotification(userName, "上线");
}
if (returnContent.contentType == "newUserOffLine") {
var userName = returnContent.userName;
NotificationHandler.showNotification(userName, "离线");
}
if (returnContent.contentType == "chatMessages") {
var messages = returnContent.messages;
var userName = document.getElementById("userNameTxt").innerHTML;
for (var i = messages.length - 1; i >= 0; i--) {
if (messages[i].msg.user.userName == userName) {
messageList += '<li><span class="chatcontent mychatcontent">' +
messages[i].msg.chatMessage + '</span><span class="' +
messages[i].msg.user.headImages + '"></span></li>';
} else {
messageList += '<li><span class="' +
messages[i].msg.user.headImages +
'"></span><span class="chatcontent">' +
messages[i].msg.chatMessage + '</span></li>';
}
}
document.getElementById('chatList').innerHTML = messageList;
}
};
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>基于Node.js实现网络聊天室</title>
<link rel="stylesheet" type="text/css" href="css/main.css" />
</head>
<body>
<div class="container">
<!-- header -->
<div class="header">
<div id="configure">
服务器地址:<input type="text" id="serverAdress" value="localhost" />
服务器端口:<input type="text" id="serverPort" value="8080" />
昵称:<input type="text" id="userName" />
<input type="button" class="login-button" onclick="loginUser()" />
</div>
<div id="chatPart" class="none">
服务器地址:<span id="serverAdressTxt"></span>
服务器端口:<span id="serverPortTxt"></span>
昵称:<span id="userNameTxt"></span>
</div>
</div>
<div class="banner"></div>
<!-- content -->
<div class="content">
<!-- left -->
<div class="left">
<div id="connectStatus">
<h3 class="gray">用户状态</h3>
<div id="connectDiv" class="offline-img"></div>
</div>
<div id="messages" class="none">
<h3 class="gray">聊天记录</h3>
<input type="hidden" id="connectionId" />
<textarea rows="3" cols="30" id="chatMessage"></textarea>
<input type="button" class="send-button" onclick="sendMessage()">
<ul id="chatList">
<li><span class="head1"></span>
<span class="chatcontent">欢迎使用聊天室</span>
</li>
</ul>
</div>
</div>
<!-- right -->
<div class="right">
<h3 class="box">在线用户</h3>
<div id="userOnLine" class="none-img"></div>
<ul id="userOnLine"></ul>
</div>
</div>
</div>
<script type="text/javascript" src="js/notification.js"></script>
<script type="text/javascript" src="js/clientWebSocket.js"></script>
</body>
</html>
结果
步骤详解
- 控制台使用npm init命令对项目初始化
- 控制台分别使用 npm install express --save-dev 命令、npm install ws --save-dev 命令来安装Express框架、npm install node-uuid --save-dev 命令来安装Express、WebSocket、node_uuid框架
- 目录结构
- 创建服务端脚本index.js ,使用WebSocket框架完成创建服务端
- 创建客户端脚本clientWebSocket.js ,完成WebSocket客户端
- 实现网络聊天室主界面index.html
- 最后在控制台中使用 node index.js 命令启动Node.js服务器,在浏览器中输入 localhost:3000 网址进入测试。
PS
以后可能会用其他工具再次实现这个网络聊天室,到时候做的就会很好用很美观啦~