Node.js第十五篇:Socket.IO
<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>第一章:认识Socket.IO<ul><li>1.1-WebSocket</li><li>1.2-Socket.IO是什么</li><li>1.3-什么是Socket</li><li>1.4-常用的API</li></ul></li><li>第二章:Socket.IO快速入门<ul><li>2.1-需求</li><li>2.2-服务端程序</li><li>2.3-客户端程序</li><li>2.4-测试</li></ul></li><li>第三章:Express中使用Socket.IO<ul><li>3.1-基本使用</li><li>3.2-发送文字和图片</li></ul></li><li>第四章:Koa中使用Socket.IO</li><li>第五章:案例-Open聊天室<ul><li>5.1-需求</li><li>5.2-页面交互流程图</li><li>5.3-代码下载</li></ul></li></ul></div><p></p><h2 id="第一章认识socketio">第一章:认识Socket.IO</h2>
<h3 id="11-websocket">1.1-WebSocket</h3>
<p><strong>传统</strong>的客户端和服务器通信协议是<strong>HTTP</strong>:客户端发起请求,服务端进行响应,<strong>服务端从不主动勾搭客户端</strong>。</p>
<p>这种模式有个明显软肋,就是同步状态。而实际应用中有大量需要客户端和服务器实时同步状态的场景,比如聊天室、股票行情、在线共享文档等都需要客户端实时拿到服务器的最新状态。</p>
<p>针对这种实时同步的需求,一种简单的方式是轮询,比如每隔5s发一次http请求去拿服务器最新的状态数据。但这种方式会存在数据延迟,浪费带宽等副作用。</p>
<p>更完美的方式是使用WebSocket,浏览器原生支持,W3C标准协议,客户端和服务器建立持久性连接可以互发消息。</p>
<h3 id="12-socketio是什么">1.2-Socket.IO是什么</h3>
<p>socket.io 是一个类库,内部封装了WebSocket,可以在浏览器与服务器之间建立实时通信。</p>
<p>如果某些旧版本的浏览器不支持WebSocket,socket.io会使用轮询代替。另外它还具有可发送二进制消息、多路复用、创建房间等特性,因此相比直接使用原生WebSocket,socket.io是更好的选择。</p>
<p>开发一个实时应用主要分两部分:服务端和客户端,socket.io分别提供了相应的模块供我们方便地调用。</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-a67f613fa5cd48ad10df3e818259fc3f808.png" alt="" loading="lazy"></p>
<h3 id="13-什么是socket">1.3-什么是Socket</h3>
<p><strong>套接字</strong>(socket)是一个抽象层,应用程序可以<strong>通过它发送或接收数据</strong>,可对其进行像对文件一样的<strong>打开、读写和关闭</strong>等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。</p>
<p><strong>作用:完成两个应用程序之间的数据传输</strong></p>
<h3 id="14-常用的api">1.4-常用的API</h3>
<p>官方文档:https://socket.io/docs/#</p>
<blockquote>
<p>服务端</p>
</blockquote>
<p><strong>io对象</strong></p>
<ul>
<li>on方法用来监听事件
<ul>
<li><code>io.on('connection',socket=>{ })</code>
<ul>
<li>connection对象监听客户端连接</li>
<li>socket表示当前进入的客户端socket对象</li>
</ul>
</li>
</ul>
</li>
<li><code>emit('事件名称',数据</code>)服务端向所有客户端发送消息
<ul>
<li>事件名称,客户端或服务端约定好向对方发送消息对方要触发的事件</li>
<li>数据,传递的信息</li>
</ul>
</li>
<li><code>io.to(roomid).emit('事件名称', 数据)</code>向指定的属于同一组的客户端发送消息
<ul>
<li>roomid 分组名称表示</li>
</ul>
</li>
</ul>
<p><strong>socket对象</strong></p>
<ul>
<li>on方法用来监听事件
<ul>
<li><code>socket.on('disconnect',()=>{})</code>
<ul>
<li>若有一个客户端端口连接,则会触发该事件</li>
</ul>
</li>
<li><code>socket.on('自定义其他事件名称',(data)=>{})</code>
<ul>
<li>自定其他事件名,客户端或服务端约定好对方发送消息时,用哪个事件触发</li>
<li>data,接收对方发送过来的数据</li>
</ul>
</li>
</ul>
</li>
<li><code>id</code>属性,socket对象的标识</li>
<li><code>emit('事件名称',数据)</code>向对应的客户端发送消息
<ul>
<li>事件名称,客户端或服务端约定好向对方发送消息对方要触发的事件</li>
<li>数据,传递的信息</li>
</ul>
</li>
<li><code>join(roomid)</code>加入某个分组
<ul>
<li>roomid,字符串,可以自定义</li>
</ul>
</li>
<li><code>leave(roomid)</code> 从某个分组中脱离</li>
<li><code>socket.broadcast.to(roomid).emit('事件名称', 数据)</code> 向所有客户端(同属一组,除了自己)发送数据</li>
</ul>
<blockquote>
<p>客户端</p>
</blockquote>
<p><strong>io对象</strong></p>
<ul>
<li>i<code>o(url)</code>连接指定的服务端,并返回一个sokcet对象
<ul>
<li>url,服务端连接地址,如:<code>http://localhost</code></li>
</ul>
</li>
</ul>
<p><strong>socket对象</strong></p>
<ul>
<li><code>socket.on('connect', function () {})</code> 客户端和服务端建立连接成功后要触发的事件</li>
<li><code>socket.on('disconnect', function () {})</code> 客户端和服务端断开连接要触发的事件(比如服务器崩溃)</li>
<li><code>socket.on('自定义其他事件名称',(data)=>{})</code>
<ul>
<li>自定其他事件名,客户端或服务端约定好对方发送消息时,用哪个事件触发</li>
<li>data,接收对方发送过来的数据</li>
</ul>
</li>
<li><code>socket.emit('事件名称',数据</code>)客户端向服务端发送消息</li>
</ul>
<h2 id="第二章socketio快速入门">第二章:Socket.IO快速入门</h2>
<p>在Node.js中使用Socket.IO</p>
<h3 id="21-需求">2.1-需求</h3>
<ul>
<li>服务端和客户端建立连接</li>
<li>客户端和服务端建立连接,并向服务端发送一条消息</li>
<li>服务端向所有客户端,发送一条消息</li>
<li>有一个客户端离线,服务端可以接受到通知</li>
</ul>
<h3 id="22-服务端程序">2.2-服务端程序</h3>
<p>导入第三方模块<code>npm install socket.io</code></p>
<pre><code class="language-javascript">const http = require("http");
const url = require("url");
const path = require("path");
const fs = require("fs");
const mime = require("mime");
// 创建服务对象
const app = http.createServer();
// 监听请求,处理静态资源
app.on("request", (req, res) => {
// 获取请求的路径
let { pathname } = url.parse(req.url, true);
// 拼接服务器上文件的物理路径
let realPath = path.join(__dirname, "public", pathname);
// 获取请求的资源类型
let type = mime.getType(realPath);
// 读取服务器本地文件
fs.readFile(realPath, (err, data) => {
if (err) {
res.writeHead(404,{"Content-type":type+";charset=utf-8"});
res.end("访问资源不存在");
return;
}
res.writeHead(200);
res.end(data);
});
});
// 【重点!!-导入socket.io并和http服务对象关联】
const io = require('socket.io')(app)
// 【重点!!-检测客户端连接进入】
io.on('connection', socket => {
console.log('一个客户端进入')
// 注册to-server事件,接收客户端向服务端发送的数据
socket.on('to-server', (data) => {
console.log('客户端说: ' + data)
// 向对应的客户端发送数据
socket.emit('to-client', '我是服务端数据')
// 向所有在线的客户端发送数据
// io.emit('to-client','我是服务端数据')
})
// 检测一个客户端断开连接
socket.on('disconnect', () => {
console.log('一个客户端离开')
})
})
// 开启端口4000
app.listen(4000);
</code></pre>
<p>服务端启动成功后,客户端可以通过<code>http://127.0.0.1:4000/socket.io/socket.io.js</code>在客户端操作socket</p>
<h3 id="23-客户端程序">2.3-客户端程序</h3>
<p>index.html</p>
<pre><code class="language-html"><input type="text" id="message"><button id="btn">发送</button>
<!-- 导入客户端socket.io.js -->
<script src="http://127.0.0.1:4000/socket.io/socket.io.js"></script>
<script>
// 创建socket对象,设置要连接的服务器url
var socket = io('http://127.0.0.1:4000');
// 注册connect事件,监听和服务是否建立了连接
socket.on('connect', function () {
console.log('客户端和服务建立了连接')
})
// 注册disconnect事件,监听和服务是否断开连接
socket.on('disconnect', function () {
console.log('客户端和服务端断开连接了');
})
// 注册to-client事件,监听服务端向客户端传送的数据
socket.on('to-client', function (data) {
console.log('服务端说:' + data);
})
// 点击按钮向服务端发送数据
btn.onclick = function() {
var val = message.value
socket.emit('to-server',val)
}
</script>
</code></pre>
<h3 id="24-测试">2.4-测试</h3>
<ol>
<li>打开多个客户端:http://127.0.0.1:4000/index.html</li>
<li>在控制台查看服务端发送的消息</li>
<li>在服务端控制台中查看客户端发送的消息</li>
</ol>
<h2 id="第三章express中使用socketio">第三章:Express中使用Socket.IO</h2>
<h3 id="31-基本使用">3.1-基本使用</h3>
<blockquote>
<p>服务端程序</p>
</blockquote>
<pre><code class="language-javascript">const express = require('express')
const path = require('path')
const app = express()
const statiPath = path.join(__dirname, './public')
app.use(express.static(statiPath))
// 【重点-通过http模块Server方法获取关联Express的服务对象】
const server = require('http').Server(app);
// 【重点-导入socket.io模块获取io对象并关联http服务对象】
const io = require('socket.io')(server);
// 【重点-检测客户端和服务端是否连接成功】
io.on('connection', socket => {
console.log('有一个客户端连接成功')
// 监听当前客户端向服务端发送的数据
socket.on('message', data => {
console.log('客户端说:' + data)
// 服务端向所有客户端发送消息
io.emit('message',data)
})
})
// 【重点-使用server监听80端口】
server.listen(80)
</code></pre>
<blockquote>
<p>客户端程序</p>
</blockquote>
<p>index.html</p>
<pre><code class="language-html"><input type="text" id="msg"><button id="btn">发送</button>
<script src="http://localhost/socket.io/socket.io.js"></script>
<script>
// 客户端创建socket对象,并配置连接服务器url
var socket = io('http://localhost')
// 监听服务端发送过来的数据
socket.on('message',function(data){
console.log(data)
})
// 点击按钮向服务端发送数据
btn.onclick = function(){
socket.emit('message',msg.value)
}
</script>
</code></pre>
<h3 id="32-发送文字和图片">3.2-发送文字和图片</h3>
<p><img src="https://oscimg.oschina.net/oscnet/up-01c6a1087b2ca53ae3c9f14d8910ad28244.png" alt="" loading="lazy"></p>
<blockquote>
<p><strong>服务端程序</strong></p>
</blockquote>
<pre><code class="language-javascript">const express = require('express')
const path = require('path')
const app = express()
const statiPath = path.join(__dirname, './public')
app.use(express.static(statiPath))
// 【重点-通过http模块Server方法获取关联Express的服务对象】
const server = require('http').Server(app);
// 【重点-导入socket.io模块获取io对象并关联http服务对象】
const io = require('socket.io')(server);
// 【重点-检测客户端和服务端是否连接成功】
io.on('connection', socket => {
console.log('有一个客户端连接成功')
// 监听当前客户端向服务端发送的数据-文本消息
socket.on('message', data => {
console.log('客户端说:' + data)
// 服务端向所有客户端发送消息
io.emit('message',data)
})
// 监听当前客户端向服务端发送的数据-文件消息
socket.on('image', data => {
// 向其他客户端发送文件
io.emit('image', data);
})
})
// 【重点-使用server监听80端口】
server.listen(80)
</code></pre>
<blockquote>
<p><strong>客户端程序</strong></p>
</blockquote>
<pre><code class="language-html"><div class="talk">
<!-- 聊天记录 -->
<div class="box">
<ul id="ul"></ul>
</div>
<!-- 发送文本消息 -->
<p>
<textarea id="msg" placeholder="请输入内容"></textarea>
<!-- 按钮 -->
<button id="btn">发送</button>
<button id="btn2">图片</button>
<input type="file" style="display:none" id="fileDom">
</p>
</div>
<script src="http://localhost/socket.io/socket.io.js"></script>
<script>
// 客户端创建socket对象,并配置连接服务器url
var socket = io('http://localhost')
// 监听服务端发送过来的数据-文本
socket.on('message', function (data) {
var li = document.createElement('li');
li.innerText = data;
ul.appendChild(li)
})
// 监听服务端发送过来的数据-文本
socket.on('image', function (data) {
var li = document.createElement('li');
li.innerHTML = '<img src="'+data+'">';
ul.appendChild(li)
})
// 点击按钮向服务端发送数据-文字
btn.onclick = function () {
socket.emit('message', msg.value)
}
// 点击按钮向服务端发送数据-图片
btn2.onclick = function () {
fileDom.click()
}
// 上传事件触发
fileDom.onchange = function() {
// 获取读取的文件
var file = fileDom.files;
// 创建fileReader对象
var reader = new FileReader();
// 读取文件内容
reader.readAsDataURL(file)
// 读取完毕后,发送到服务端
reader.onload = function(ev){
// console.log(reader.result)
// 发送文件数据
socket.emit('image',reader.result)
}
}
</script>
</code></pre>
<h2 id="第四章koa中使用socketio">第四章:Koa中使用Socket.IO</h2>
<p>导入第三方模块 <code>npm install koa-socket-2</code></p>
<p>参考文档:https://www.npmjs.com/package/koa-socket-2</p>
<blockquote>
<p>服务端程序</p>
</blockquote>
<pre><code class="language-javascript">const koa = require('koa')
const router = require('koa-router')()
const path = require('path')
const static = require('koa-static')
// 【导入koa-socket-2模块】
const IO = require('koa-socket-2');
// 创建koa服务对象
const app = new koa()
// 【创建IO对象】
const io = new IO();
// 【和koa服务对象关联】
io.attach(app)
// 定义roomid,实现分组
let roomid = 'group';
// 【监听客户端连接服务】
app._io.on('connection', socket => {
console.log('有新的客户端进入')
// 新的客户端socket加入组中
socket.join(roomid);
// 【监听客户端发送的数据】
socket.on('message', data => {
console.log('客户端发送的数据是:' + data)
})
// 服务端向当前客户端发送数据
socket.emit('message', '对您广播:您好,同志')
// 服务端向所有客户端发送数据
app._io.emit('message', '全员广播:同志们,咱们大家好')
// 服务端向所有客户端(同属一组,除了自己)发送数据
socket.broadcast.to('group').emit('message', '小组中的朋友们,大家好')
// 服务端向所有客户端(同属一组,包括了自己)发送数据
app._io.to('group').emit('message', '小组中的朋友们,大家好2')
// 检测一个客户端离线
socket.on('disconnect', () => {
// socket.id 获取socket的唯一标识
console.log('id为' + socket.id + '客户端离线了');
})
});
// 配置路由和静态资源
app.use(router.routes())
app.use(router.allowedMethods());
app.use(static(path.join(__dirname,'./public')))
app.listen(80)
</code></pre>
<blockquote>
<p>客户端程序</p>
</blockquote>
<pre><code class="language-html"><input type="text" id="text">
<button id="btn">向服务端发送数据</button>
<script src="http://localhost/socket.io/socket.io.js"></script>
<script>
// 连接服务端,并创建客户端socket对象
var socket = io('http://localhost');
// 点击按钮向服务端发送数据
btn.onclick = function() {
var val = text.value;
socket.emit('message',val);
}
// 监听服务端发送的数据
socket.on('message',function(data){
console.log('服务端说:' + data);
})
</script>
</code></pre>
<h2 id="第五章案例-open聊天室">第五章:案例-Open聊天室</h2>
<h3 id="51-需求">5.1-需求</h3>
<blockquote>
<p>登录界面</p>
</blockquote>
<ol>
<li>用户进入登录页面</li>
<li>用户输入昵称,点击进入聊天室</li>
<li>后端检测,该用户是否存在(是否已经存在有该昵称的socket)</li>
<li>存在,则提示用户更换用户名</li>
<li>不存在,则允许用户进入聊天室,并在后端系统中保存该用户的socket</li>
</ol>
<blockquote>
<p>聊天界面</p>
</blockquote>
<ol>
<li>功能1:展示出所有在线用户</li>
<li>功能2:群聊</li>
<li>功能3:私聊</li>
<li>功能4:上线消息提醒</li>
<li>功能5:发送图片</li>
<li>功能6:新消息提醒</li>
</ol>
<h3 id="52-页面交互流程图">5.2-页面交互流程图</h3>
<p>通过流程图直观了解业务</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-1bc2f8ed2bf494d4074bacc0f66a878d8e6.png" alt="" loading="lazy"></p>
<h3 id="53-代码下载">5.3-代码下载</h3>
<p>代码没有做详细优化,后续会更新</p>
<p>https://gitee.com/lpl666/openliaotianshi.git</p><br><br>
来源:https://www.cnblogs.com/lpl666/p/12986612.html
頁:
[1]