一、开发环境及参考书籍
IDE是vscode,已经配置好node环境等
参考书籍:《node.js入门经典》
二、安装模块
1.新建工程文件夹
2.新建package.json文件,代码如下
{
"name": "socketio-express-example",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "^4.16.3",
"socket.io": "^2.1.1"
}
}
注:(1)scripts中可以写入各种命令,具体情况可以在网上搜索,在这里,写入 "start":"node app.js" 后可以在vscode中选择npm start方式运行,其实等同于在命令行中执行node app.js
(2)dependencies中包含了项目中使用到的各种模块,node是一个开源项目,这些模块都是各位大佬以及编程爱好者贡献的。格式为"名称":"版本号",如果不清楚版本号是什么的话可以直接到github上找源码,例如socket.io的readme文档中包括了基本信息,链接https://github.com/socketio/socket.io
(3)express模块用于搭建服务器,socket.io模块用于前后端传递消息
3.运行npm install安装所需模块,安装后可以在工程文件夹下看到node_modules文件夹,里面存放了涉及的所有模块
三、搭建服务器
1.新建app.js文件,写入如下代码,可以创建一个本地服务器,端口号是3000
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var path = require('path');
server.listen(3000);
//路由,访问"127.0.0.1:3000/"时调用
app.use('/', function(req, res){
res.sendFile(path.join(__dirname, '/index.html'));
});
2.在工程目录下新建index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Socket.IO Express Example</title>
</head>
<body>
<h1>Socket.IO Express Example</h1>
</body>
</html>
3.使用npm start命令运行任务,可以看到服务器已经搭建完成
四、socket.io模块
1. 使用socket.io模块可以实现服务器和浏览器之间的通信。在使用前必须将其添加到服务器端的node.js和客户端的jQuery中。
在node.js中引入io模块
var io = require('socket.io').listen(server);
在index.html中引入socket.io
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect();
</script>
注:index.html中的文件路径/socket.io/socket.io.js是由socket.io库自动提供的,只要是在同一服务器上运行服务端和客户端
2. 主要用法
(1)服务端中连接与断开连接的监听,所有的其他socket.io操作都应在connetion的回调函数中进行
io.sockets.on('connection', function(socket){
//连接后的代码
socket.on('disconnect', function(){
//断开连接后
});
});
(2)使用sockets.on('data', function(){ })监听数据,使用socket.emit('data', value)发送数据
前端的数据发送和接收
socket.emit('data', dataValue);
socket.on('message', function(message){ });
服务端数据的发送和接收
socket.emit('message', messageValue);
socket.on('data', function(data){ });
注:dataValue的值可以为各种数据类型,比如对象形式,String形式等
五、业务逻辑
效果图如下:
未注册时界面需要输入昵称,注册后界面包括消息框、在线人员昵称显示,对话内容显示。
1. 前端HTML
<form id="set-nickname">
<label for="nickname">Nickname:</label>
<input type="text" id="nickname">
<input type="submit">
</form>
<form id="send-message">
<textarea id="message"></textarea>
<input type="submit">
</form>
<section id="nicknames">
<ul></ul>
</section>
<section id="messages">
</section>
主要设置了2个表单,一个输入昵称,一个输入消息
2. 前端Javascript
var socket = io.connect();
jQuery(function($){
var nickname = $('#nickname');
var setNicknameForm = $('#set-nickname');
var nicknameList = $('#nicknames ul');
var messageForm = $('#send-message');
var message = $('#message');
var messages = $('#messages');
//注册昵称
setNicknameForm.submit(function(event){
event.preventDefault();
socket.emit('nickname', nickname.val(), function(data){
if(data){
console.log('Nickname set successfully');
setNicknameForm.hide();
messageForm.show();
}
else{
setNicknameForm.prepend('<p>Sorry, that nickname is already taken.</p>');
}
});
});
//提交输入的消息
messageForm.submit(function(event){
event.preventDefault();
socket.emit('user message', message.val());
message.val('').focus();
});
socket.on('nicknames', function(data){
var html = '';
for(var i=0; i<data.length; i++){
html += '<li>' + data[i] + '</li>';
}
nicknameList.empty().append(html);
});
//在消息列表更新消息
socket.on('user message', function(data){
messages.append('<p><strong>'+data.nickname+'</strong>'+data.message+'</p>');
});
});
主要是对两个表单内容进行处理,使用jQuery获取页面元素然后使用socket.emit传递数据到服务端。此外通过socket.on实时监听其他用户传来的消息并显示到页面上
3. 服务端代码
//使用数组存放用户姓名
var nicknames = [];
io.sockets.on('connection', function(socket){
socket.on('nickname', function(data, callback){
if(nicknames.indexOf(data) != -1){
callback(false);
}
else{
callback(true);
nicknames.push(data);
socket.nickname = data;
console.log('Nicknames are: ' + nicknames);
io.sockets.emit('nicknames', nicknames);
}
});
socket.on('user message', function(data){
io.sockets.emit('user message', {
nickname: socket.nickname,
message: data
});
});
socket.on('disconnect', function(){
if(!socket.nickname)
return;
if(nicknames.indexOf(socket.nickname) > -1){
nicknames.splice(nicknames.indexOf(socket.nickname), 1);
}
console.log('Nicknames are: ' + nicknames);
io.sockets.emit('nicknames', nicknames);
});
});
和前端代码基本是对应的,断开连接后从昵称列表中删掉该昵称。服务端就像是一个中介,因为客户端和客户端不能直接交换信息,必须传递到服务端后由服务端再把消息分配给所有客户端。
六、问题所在
1. 新加入的用户看不到之前的消息。
这是因为其实所有的消息都是由提交过后添加到服务端的网页上的,比如A发了个消息,那么服务端接收到A的消息后直接广播给所有在线的客户端,客户端将这条消息添加到页面上,消息根本就没有保存,因此C登录后获取不了。
2. 刷新后直接掉线
因为刷新后会重新访问 / 路由,重新调用index.html文件