本文参考1 WebSocket之仿QQWeb即时聊天系统(上)
本文参考2 WebSocket之仿QQWeb即时聊天系统(下)
本文参考3 Node.js 教程_菜鸟
本文参考4 socket.io 概述
摇骰子摇到了1,所以选择了仿QQWeb即时聊天系统
功能要求
- 实现
Web的点对点即时的文本消息聊天功能。 - 实现
Web的表情的发送、接收和显示功能。 - 实现
Web的图片的发送、接收和显示功能。 - 实现本地消息的存储,在离线的时候也能加载和查看历史消息;
- 要求使用
WebSocket;
功能分析
就是能发 文本、表情、图片。点对点。
还使用了WebSocket。
WebSocket ?
WebSocket
WebSocket 是独立的、创建在 TCP 上的协议。
Websocket 通过HTTP/1.1 协议的101状态码进行握手。
为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(handshaking)。
WebSocket的出现,使得浏览器具备了实时双向通信的能力。
WebSocket可以在浏览器里使用- 支持双向通信
- 使用很简单
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。
WebSocket是一个很棒的概念,我单独拿出来写。
WebSocket
创建 对象
以下 API 用于创建 WebSocket 对象。
var Socket = new WebSocket(url, [protocol] );
以上代码中的第一个参数 url, 指定连接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。
属性
以下是 WebSocket 对象的属性。假定我们使用了以上代码创建了 Socket 对象:
| 属性 | 描述 |
|---|---|
| Socket.readyState | 只读属性 readyState 表示连接状态,可以是以下值:0 – 表示连接尚未建立。1 – 表示连接已建立,可以进行通信。2 – 表示连接正在进行关闭。3 – 表示连接已经关闭或者连接不能打开。 |
| Socket.bufferedAmount | 只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。 |
WebSocket 事件
以下是 WebSocket 对象的相关事件。假定我们使用了以上代码创建了 Socket 对象:
| 事件 | 事件处理程序 | 描述 |
|---|---|---|
| open | Socket.onopen | 连接建立时触发 |
| message | Socket.onmessage | 客户端接收服务端数据时触发 |
| error | Socket.onerror | 通信发生错误时触发 |
| close | Socket.onclose | 连接关闭时触发 |
WebSocket 方法
以下是 WebSocket 对象的相关方法。假定我们使用了以上代码创建了 Socket 对象:
| 方法 | 描述 |
|---|---|
| Socket.send() | 使用连接发送数据 |
| Socket.close() | 关闭连接 |
为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息"Upgrade: WebSocket"表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。
一些准备工作
根据大佬RongLin02的博客做一些准备工作。
首先是对于WebSocket的了解。以上已经介绍了。
然后是安装Node.js,你也可以进入 官网 下载。学习页面。
NPM的安装,在上一步极有可能已经附带完成。
socket.io
是一个WebSocket库,用起来很方便。
库所以只需要引入而不需要下载。
在项目根目录 右键–在集成终端中打开,
执行以下代码:
npm install jquery
npm install socketio
npm install express
会生成一个node_modules文件夹用来存放安装好的模块,一些配置信息会生成在package-lock.json和package.json文件中。
使用方法
在中文文档里面发现第一个文档 socket.io 快速入门教程——聊天应用 就和我们的任务很像。
按照其流程一步一步来。
常用方法
socket.emit('A',value);
这个是发送方法,A是名字,叫什么名字都行,value就是要发送的值,可以是服务器给客户端发送,也可以是客户端给服务器发送,类型也可以是一个对象。
socket.on('A',function(data){
console.log(data);
});
有发送就有接受,服务器或者客户端会监听,如果有终端通过 名字 A 发送,就会获取到发送的数据,并且把value存到data中,所以说上边的那个data的值就是emit的value。
emit和on一对一。
运行方法
根目录下 右键-终端运行 输入 node index.js
或 编译器左边项目目录,右键 –打开于 –终端。在编译器下方的窗口输入指令。

REPL 命令
ctrl + c– 退出当前终端。ctrl + c 按下两次– 退出 Node REPL。ctrl + d– 退出 Node REPL.向上/向下 键– 查看输入的历史命令tab 键– 列出当前命令
学习过程
1. 服务器端的编写
创建服务器
使用 http.createServer() 方法创建服务器,并使用 listen 方法绑定端口3000 。 函数通过 request, response 参数来接收和响应数据。
在项目根目录 新建一个 index.js 文件来创建应用。
var app = require('express')();
var http = require('http').Server(app);
app.get('/', function(req, res){
console.log("主页 POST 请求");
res.send('<h1>Hello world</h1>');
});
var server = app.listen(3000, function () {
var host = server.address().address
var port = server.address().port
console.log("访问地址为 http://%s:%s", host, port)
})
这段代码作用如下:
Express初始化app作为HTTP服务器的回调函数 (见第 2 行)。- 定义了一个路由
/来处理首页访问。在终端输出"主页 POST 请求",给客户端返回'<h1>Hello world</h1>'。 - 使 http 服务器监听
端口 3000。也就是说这个代码只对端口3000有效。
路由/其实是域名后面的东西。如果访问 localhost:3000/,因为 域名后面只有“/”,所以匹配了这个路由。
运行node index.js
2. Node.js
模块
引入模块
在 Node.js 中,引入一个模块非常简单。
创建 hello.js 文件,代码如下:
//hello.js
exports.world = function() {
console.log('Hello World');
}
//world模块就暴露了,调用见下
创建一个 main.js 文件并引入 hello 模块,代码如下:
//main.js
var hello = require('./hello');//引入
hello.world();//调用
以上实例中,代码 require('./hello') 引入了当前目录下的 hello.js 文件(./ 为当前目录,node.js 默认后缀为 js)。
Node.js 提供了 exports 和 require 两个对象。
exports 是模块公开的接口。
require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。
有时候我们只是想把一个对象封装到模块中,格式如下:
module.exports = function() {
// ...
}
例如:
//hello.js
function Hello() {
var name;
this.setName = function(thyName) {
name = thyName;
};
this.sayHello = function() {
console.log('Hello ' + name);
};
};
module.exports = Hello; //暴露接口
这样就可以直接获得这个对象了:
//main.js
var Hello = require('./hello');
hello = new Hello();
hello.setName('BYVoid');
hello.sayHello();
Node.js 中自带了一个叫做 http 的模块,我们在我们的代码中请求它并把返回值赋给一个本地变量。
这把我们的本地变量变成了一个拥有所有 http 模块所提供的公共方法的对象。
模块的加载
在路径 Y 下执行 require(X) 语句执行顺序:
1. 如果 X 是内置模块
a. 返回内置模块
b. 停止执行
2. 如果 X 以 '/' 开头
a. 设置 Y 为文件根路径
3. 如果 X 以 './' 或 '/' or '../' 开头
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
4. LOAD_NODE_MODULES(X, dirname(Y))
5. 抛出异常 "not found"
LOAD_AS_FILE(X) //作为文件加载
1. 如果 X 是一个文件, 将 X 作为 JavaScript 文本载入并停止执行。
2. 如果 X.js 是一个文件, 将 X.js 作为 JavaScript 文本载入并停止执行。
3. 如果 X.json 是一个文件, 解析 X.json 为 JavaScript 对象并停止执行。
4. 如果 X.node 是一个文件, 将 X.node 作为二进制插件载入并停止执行。
LOAD_INDEX(X) //加载索引
1. 如果 X/index.js 是一个文件, 将 X/index.js 作为 JavaScript 文本载入并停止执行。
2. 如果 X/index.json 是一个文件, 解析 X/index.json 为 JavaScript 对象并停止执行。
3. 如果 X/index.node 是一个文件, 将 X/index.node 作为二进制插件载入并停止执行。
LOAD_AS_DIRECTORY(X) //作为目录加载
1. 如果 X/package.json 是一个文件,
a. 解析 X/package.json, 并查找 "main" 字段。
b. let M = X + (json main 字段)
c. LOAD_AS_FILE(M)
d. LOAD_INDEX(M)
2. LOAD_INDEX(X)
LOAD_NODE_MODULES(X, START) //加载节点模块
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
a. LOAD_AS_FILE(DIR/X)
b. LOAD_AS_DIRECTORY(DIR/X)
NODE_MODULES_PATHS(START) //节点模块路径
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
a. if PARTS[I] = "node_modules" CONTINUE
b. DIR = path join(PARTS[0 .. I] + "node_modules")
c. DIRS = DIRS + DIR
d. let I = I - 1
5. return DIRS
回调函数
回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数。
函数
一个函数可以作为另一个函数的参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。
举例来说,你可以这样做:
function say(word) {
console.log(word);
}
function execute(someFunction, value) {
someFunction(value);
}
execute(say, "Hello");
匿名函数
我们可以把一个函数作为变量传递。但是我们不一定要绕这个”先定义,再传递”的圈子,我们可以直接在另一个函数的括号中定义和传递这个函数:
function execute(someFunction, value) {
someFunction(value);
}
execute(function(word){ console.log(word) }, "Hello");
我们在 execute 接受第一个参数的地方直接定义了我们准备传递给 execute 的函数。
用这种方式,我们甚至不用给这个函数起名字,这也是为什么它被叫做匿名函数 。
全局对象
__filename
__filename 表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。
__dirname
__dirname 表示当前执行脚本所在的目录。
文件系统
Node 导入文件系统模块(fs)语法如下所示:
var fs = require("fs")
Node.js Web 模块
使用 Node 创建 Web 服务器
Node.js 提供了 http 模块,http 模块主要用于搭建 HTTP 服务端和客户端,使用 HTTP 服务器或客户端功能必须调用 http 模块,代码如下:
var http = require('http');
Node.js Express 框架
Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。
使用 Express 可以快速地搭建一个完整功能的网站。
Express 框架核心特性:
- 可以设置中间件来响应 HTTP 请求。
- 定义了路由表用于执行不同的 HTTP 请求动作。
- 可以通过向模板传递参数来动态渲染 HTML 页面。
请求和响应
Express 应用使用回调函数的参数: request 和 response 对象来处理请求和响应的数据。
app.get('/', function (req, res) {
// --
})
request 和 response 对象的具体介绍:
Request 对象 – request 对象表示 HTTP 请求,包含了请求查询字符串,参数,内容,HTTP 头部等属性。
常见属性有:
- req.app:当callback为外部文件时,用req.app访问express的实例
- req.baseUrl:获取路由当前安装的URL路径
- req.body / req.cookies:获得「请求主体」/ Cookies
- req.fresh / req.stale:判断请求是否还「新鲜」
- req.hostname / req.ip:获取主机名和IP地址
- req.originalUrl:获取原始请求URL
- req.params:获取路由的parameters
- req.path:获取请求路径
- req.protocol:获取协议类型
- req.query:获取URL的查询参数串
- req.route:获取当前匹配的路由
- req.subdomains:获取子域名
- req.accepts():检查可接受的请求的文档类型
- req.acceptsCharsets / req.acceptsEncodings / req.acceptsLanguages:返回指定字符集的第一个可接受字符编码
- req.get():获取指定的HTTP请求头
- req.is():判断请求头Content-Type的MIME类型
Response 对象 – response 对象表示 HTTP 响应,即在接收到请求时向客户端发送的 HTTP 响应数据。
常见属性有:
- res.app:同req.app一样
- res.append():追加指定HTTP头
- res.set()在res.append()后将重置之前设置的头
- res.cookie(name,value [,option]):设置Cookie
- opition: domain / expires / httpOnly / maxAge / path / secure / signed
- res.clearCookie():清除Cookie
- res.download():传送指定路径的文件
- res.get():返回指定的HTTP头
- res.json():传送JSON响应
- res.jsonp():传送JSONP响应
- res.location():只设置响应的Location HTTP头,不设置状态码或者close response
- res.redirect():设置响应的Location HTTP头,并且设置状态码302
- res.render(view,[locals],callback):渲染一个view,同时向callback传递渲染后的字符串,如果在渲染过程中有错误发生next(err)将会被自动调用。callback将会被传入一个可能发生的错误以及渲染后的页面,这样就不会自动输出了。
- res.send():传送HTTP响应
- res.sendFile(path [,options] [,fn]):传送指定路径的文件 -会自动根据文件extension设定Content-Type
- res.set():设置HTTP头,传入object可以一次设置多个头
- res.status():设置HTTP状态码
- res.type():设置Content-Type的MIME类型
app.use
app.use(path,callback)
app.use(path,callback)中的callback既可以是router对象又可以是函数。
app.get(path,callback)中的callback只能是函数。
当一个路由有好多个子路由时用app.use(path,router)
例子:
http://localhost:3000/home/one
http://localhost:3000/home/two
http://localhost:3000/home/three
路由/home后面有三个子路由紧紧跟随,分别是/one,/two,/three,如果使用app.get(),则要不断的重复,很麻烦,也不利用区分。
我们可以创建一个router.js专门用来一个路由匹配多个子路由。
var express = require('express')
var router = express.Router()
router.get("/",(req,res)=>{
res.send("/")
})
router.get("/one",(req,res)=>{
res.send("one")
})
router.get("/two",(req,res)=>{
res.send("second")
})
router.get("/three",(req,res)=>{
res.send("three")
})
module.exports = router;
//module.exports把router暴露出去了。
在app.js中导入router.js
var express = require('express')
var router = require("./router")
var app = express()
app.use('/home',router) //router路由对象中的路由都会匹配到"/home"路由后面
app.get('/about', function (req, res) {
console.log(req.query)
res.send('你好,我是 Express!')
})
app.listen(3000, function () {
console.log('app is running at port 3000.')
})
app.use(express.static(‘public’));
为了提供对静态资源文件(图片,css,js文件)的服务,请使用Express内置的中间函数express.static。
传递一个包含静态资源的目录给express.static中间件用于立即开始提供文件。 比如用以下代码来提供public目录下的图片、css文件和js文件:
app.use(express.static('public'));
如果前台想请求后台public目录下images/08.jpg静态的图片资源
通过: http://localhost:3000/images/08.jpg
通过多次使用express.static中间件来添加多个静态资源目录:
app.use(express.static('public'));
app.use(express.static('file'));
Express将会按照你设置静态资源目录的顺序来查看静态资源文件。
为了给静态资源文件创建一个虚拟的文件前缀(文件系统中不存在),可以使用express.static函数指定一个虚拟的静态目录,如下:
app.use('/static', express.static('public'));
现在你可以使用‘/static’作为前缀来加载public文件夹下的文件了。
比如: http:// localhost:3000/static/image/kitten.jpg
get/post
get
“读取“一个资源。比如Get到一个html文件。反复读取不应该对访问的数据有副作用。
post
例如,在页面里<form> 标签会定义一个表单。点击其中的submit元素会发出一个POST请求让服务器做一件事。这件事往往是有副作用的,不幂等的。
post是一种提交。
4. 客户端的编写
在上面的这个代码里面,我们对访问 localhost:3000 采用 res.send() 方法,让其返回一个网页。这样显然对于前端并不友好。
替代的方法是新建一个 index.html 文件作为服务器响应。
现在我们用 sendFile 来重构之前的回调:
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
__dirname指的是当前文件所在文件夹的绝对路径。
index.html 内容如下:
<!doctype html>
<html>
<head>
<title>Socket.IO chat</title> <!--网页名称-->
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
</style>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
</body>
</html>
- margin 属性为“外边距”,指的是围绕在元素边框的空白区域;
- Padding 属性为“内边距”,定义元素边框与元素内容之间的空间。
- box-sizing 是设置盒子模型用的,他其实是设置容器最终的尺寸的计算方法,盒子的计算方法如下: width(宽度) + padding(内边距) + border(边框) = 元素实际宽度
height(高度) + padding(内边距) + border(边框) = 元素实际高度 上面的计算方法是按照box-sizing:content-box来说的,根据这种设置办法是,先规定了显示区域的大小,在加上边框和内边距就是元素最终显示的大小 还有另外一种模式,就是box-sizing:border-box,他的使用方式就是,先规定了最终显示的区域大小,实际内容展示区域是根据最终区域大小减去边框和内边距,自适应比较强,有时候比较方便页面布局一些。 - font 规定文本的字体、字体尺寸、字体颜色。
日后再补。
5. 集成 Socket.IO
Socket.IO 由两部分组成:
- 一个服务端用于集成 (或挂载) 到 Node.JS HTTP 服务器:
socket.io - 一个加载到浏览器中的客户端:
socket.io-client
开发环境下, socket.io 会自动提供客户端。
Socket.IO 的核心理念就是允许发送、接收任意事件和任意数据。任意能被编码为 JSON 的对象都可以用于传输。
6. jQuery
$就是jQuery的别称。
而jQuery就是jQuery库提供的一个函数.(好像也不仅仅只是函数, 因为还有 $.ajax(options) 这样的使用,等同 jQuery.ajax(options))。
这个函数的作用是根据 () 里的参数进行查找和选择html文档中的元素, 函数作用之一就是GetElementByID的代替,但()内不仅可以是ID,还可以是各类选择器。
比如:
$(document)就是 选取整个文档对象。
7. HTML

- <!DOCTYPE html> 声明为 HTML5 文档
- <html> 元素是 HTML 页面的根元素
- <head> 元素包含了文档的元(meta)数据,如 <meta charset=“utf-8”> 定义网页编码格式为 utf-8。
- <title> 元素描述了文档的标题
- <body> 元素包含了可见的页面内容
- <h1> 元素定义一个大标题
- <p> 元素定义一个段落
学习过程中的代码
前期的学习到这里结束。
接下来放一段到此为止的代码。
服务端:
const { data } = require('jquery');
const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http);
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
console.log("主页 get 请求");
});
io.on('connection', function(socket){
console.log("一位用户连接");
socket.on('chat message', function(msg){
console.log('message: ' + msg);
io.emit('chat message', msg);
});
socket.on('disconnect', function(){
console.log('用户关闭连接');
});
});
http.listen(3000, function(){
console.log('http://127.0.0.1:3000');
});
客户端
<!doctype html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font: 13px Helvetica, Arial; }
form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages li { padding: 5px 10px; }
#messages li:nth-child(odd) { background: #eee; }
</style>
</head>
<body>
<ul id="messages"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
</body>
<script src="/socket.io/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-1.11.1.js" rel="external nofollow" ></script>
<script>
$(function () {
var socket = io();
$('form').submit(function(){
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat message', function(msg){
$('#messages').append($('<li>').text(msg));
});
});
</script>
</html>
文件结构

接下来正式开始。我将在上面代码的基础上修改。
开始
本地数据库
建立数据库

数据库建表

index.js文件添加依赖及定义,现在定义部分如下
const { data } = require('jquery');
const express=require('express');
const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http);
const mysql = require('mysql');
const url = require('url');
const fs = require('fs');
const { Console } = require('console');
const { query } = require('express');
const PORT = 3000;
后端代码
const { data } = require('jquery');
const express=require('express');
const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http);
const mysql = require('mysql');
const url = require('url');
const fs = require('fs');
const { Console } = require('console');
const { query } = require('express');
const Front=__dirname +"/Front/";
const PORT = 3000;
const _cilents=[]; //在线的名称
//数据库连接
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mhqdcqxs',
database: 'ChatProject'
});
//从数据库中查询
function select_user(data,callback){
//连接数据库开始查询
let sql = 'SELECT * FROM user where ac = \''+data.username+'\';';
connection.query(sql,function (err, result) {
if(err){
console.log('[SELECT ERROR] - ',err.message);
callback(null)
//如果查不到就返回null
}
//用回调函数表示执行完了。回调函数是哪个?取决于传参。
//查到了就返回结果
callback(result);
});
}
//插入数据库
function insert_user(data){
let sql ='INSERT INTO user VALUES (\''+data.username+'\',\''+data.password+'\');';
connection.query(sql,(err,result)=>{
if(err){
console.log('[INSERT ERROR] - ',err.message);
return;
};
});
}
app.get('/', function(req, res){
res.sendFile(Front + 'index.html');
console.log("主页 get 请求");
});
io.on('connection', function(socket){
socket.on('login',function(data){
//寻找是否在线
var t_cilent = _cilents.find(item => item == data.username);
if(t_cilent){
socket.emit('loginFail','该用户已经登录');
return ;
}
//从数据库中寻找
select_user(data,result=>{
if(result.length){
if(result[0].pw != data.password){
socket.emit('loginFail','密码错误!');
return ;
}
}
else{
insert_user(data);
}
//记录已经登录的用户
_cilents.push(data.username);
socket.emit('loginSuccess',_cilents);
//全局广播,来了新用户
io.emit('updateUser', _cilents);
socket.username = data.username;
})
});
// 用户断开连接的功能
socket.on('disconnect', () => {
// 把当前用户的信息删除
// 找到断开连接的用户的下标
const idx =_cilents.findIndex(item => item == socket.username)
// 删除函数,第一个参数是下标,第二个是距离,也就是删几个元素
_cilents.splice(idx, 1);
// 告诉所有人,用户发生更新
io.emit('updateUser', _cilents)
});
socket.on('sendMessage',data=>{
var toSocket = null
//找到接收者在io.sockets.sockets中的位置。
for (const key in io.sockets.sockets) {
if (io.sockets.sockets[key].username == data.toName) {
toSocket = key
break
}
}
if (toSocket) {
// 发送给指定用户
socket.to(toSocket).emit('receiveMessage', {
msg:data.msg,
fromName:data.name,
time : data.time,
name:data.toName
})
}
//将数据存入文件中
let url = './history/'+data.name+'To'+data.toName+'.txt';
fs.appendFile(url,'\n'+JSON.stringify(data), err => {if (err) {console.log(err)}});
if(data.toName != data.name){
let t_url = './history/'+data.toName+'To'+data.name+'.txt';
fs.appendFile(t_url,'\n'+JSON.stringify(data), err => {if (err) {console.log(err)}});
}
});
socket.on('sendImage',data=>{
var toSocket = null
//console.log(io.sockets.sockets)
for (const key in io.sockets.sockets) {
if (io.sockets.sockets[key].username == data.toName) {
toSocket = key
break
}
}
//console.log(data);
if (toSocket) {
// 发送给指定用户
socket.to(toSocket).emit('receiveImage', {
image:data.image,
fromName:data.name,
time : data.time,
name:data.toName
})
}
//将数据存入文件中
let url = './history/'+data.name+'To'+data.toName+'.txt';
fs.appendFile(url,'\n'+JSON.stringify(data), err => {if (err) {console.log(err)}});
if(data.toName != data.name){
let t_url = './history/'+data.toName+'To'+data.name+'.txt';
fs.appendFile(t_url,'\n'+JSON.stringify(data), err => {if (err) {console.log(err)}});
}
})
});
http.listen(PORT, function(){
console.log('http://127.0.0.1:'+PORT+'/');
});
后面没有了,开发时间太紧没有继续记录,但以上作为一个前置步骤已经比较完整。
