zoukankan      html  css  js  c++  java
  • Node.js第十五篇:Socket.IO

    第一章:认识Socket.IO

    1.1-WebSocket

    传统的客户端和服务器通信协议是HTTP:客户端发起请求,服务端进行响应,服务端从不主动勾搭客户端

    这种模式有个明显软肋,就是同步状态。而实际应用中有大量需要客户端和服务器实时同步状态的场景,比如聊天室、股票行情、在线共享文档等都需要客户端实时拿到服务器的最新状态。

    针对这种实时同步的需求,一种简单的方式是轮询,比如每隔5s发一次http请求去拿服务器最新的状态数据。但这种方式会存在数据延迟,浪费带宽等副作用。

    更完美的方式是使用WebSocket,浏览器原生支持,W3C标准协议,客户端和服务器建立持久性连接可以互发消息。

    1.2-Socket.IO是什么

    socket.io 是一个类库,内部封装了WebSocket,可以在浏览器与服务器之间建立实时通信。

    如果某些旧版本的浏览器不支持WebSocket,socket.io会使用轮询代替。另外它还具有可发送二进制消息、多路复用、创建房间等特性,因此相比直接使用原生WebSocket,socket.io是更好的选择。

    开发一个实时应用主要分两部分:服务端和客户端,socket.io分别提供了相应的模块供我们方便地调用。

    1.3-什么是Socket

    套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。

    作用:完成两个应用程序之间的数据传输

    1.4-常用的API

    官方文档:https://socket.io/docs/#

    服务端

    io对象

    • on方法用来监听事件
      • io.on('connection',socket=>{ })
        • connection对象监听客户端连接
        • socket表示当前进入的客户端socket对象
    • emit('事件名称',数据) 服务端向所有客户端发送消息
      • 事件名称,客户端或服务端约定好向对方发送消息对方要触发的事件
      • 数据,传递的信息
    • io.to(roomid).emit('事件名称', 数据) 向指定的属于同一组的客户端发送消息
      • roomid 分组名称表示

    socket对象

    • on方法用来监听事件
      • socket.on('disconnect',()=>{})
        • 若有一个客户端端口连接,则会触发该事件
      • socket.on('自定义其他事件名称',(data)=>{})
        • 自定其他事件名,客户端或服务端约定好对方发送消息时,用哪个事件触发
        • data,接收对方发送过来的数据
    • id属性,socket对象的标识
    • emit('事件名称',数据) 向对应的客户端发送消息
      • 事件名称,客户端或服务端约定好向对方发送消息对方要触发的事件
      • 数据,传递的信息
    • join(roomid) 加入某个分组
      • roomid,字符串,可以自定义
    • leave(roomid) 从某个分组中脱离
    • socket.broadcast.to(roomid).emit('事件名称', 数据) 向所有客户端(同属一组,除了自己)发送数据

    客户端

    io对象

    • io(url) 连接指定的服务端,并返回一个sokcet对象
      • url,服务端连接地址,如:http://localhost

    socket对象

    • socket.on('connect', function () {}) 客户端和服务端建立连接成功后要触发的事件
    • socket.on('disconnect', function () {}) 客户端和服务端断开连接要触发的事件(比如服务器崩溃)
    • socket.on('自定义其他事件名称',(data)=>{})
      • 自定其他事件名,客户端或服务端约定好对方发送消息时,用哪个事件触发
      • data,接收对方发送过来的数据
    • socket.emit('事件名称',数据) 客户端向服务端发送消息

    第二章:Socket.IO快速入门

    在Node.js中使用Socket.IO

    2.1-需求

    • 服务端和客户端建立连接
    • 客户端和服务端建立连接,并向服务端发送一条消息
    • 服务端向所有客户端,发送一条消息
    • 有一个客户端离线,服务端可以接受到通知

    2.2-服务端程序

    导入第三方模块npm install socket.io

    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);
    

    服务端启动成功后,客户端可以通过http://127.0.0.1:4000/socket.io/socket.io.js在客户端操作socket

    2.3-客户端程序

    index.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>
    

    2.4-测试

    1. 打开多个客户端:http://127.0.0.1:4000/index.html
    2. 在控制台查看服务端发送的消息
    3. 在服务端控制台中查看客户端发送的消息

    第三章:Express中使用Socket.IO

    3.1-基本使用

    服务端程序

    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)
    
    

    客户端程序

    index.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>
    

    3.2-发送文字和图片

    服务端程序

    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)
    
    

    客户端程序

      <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[0];
         // 创建fileReader对象
         var reader = new FileReader();
         // 读取文件内容
         reader.readAsDataURL(file)
         // 读取完毕后,发送到服务端
         reader.onload = function(ev){
           // console.log(reader.result)
           // 发送文件数据
           socket.emit('image',reader.result)
         }
        }
      </script>
    

    第四章:Koa中使用Socket.IO

    导入第三方模块 npm install koa-socket-2

    参考文档:https://www.npmjs.com/package/koa-socket-2

    服务端程序

    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)
    
    

    客户端程序

      <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>
    

    第五章:案例-Open聊天室

    5.1-需求

    登录界面

    1. 用户进入登录页面
    2. 用户输入昵称,点击进入聊天室
    3. 后端检测,该用户是否存在(是否已经存在有该昵称的socket)
    4. 存在,则提示用户更换用户名
    5. 不存在,则允许用户进入聊天室,并在后端系统中保存该用户的socket

    聊天界面

    1. 功能1:展示出所有在线用户
    2. 功能2:群聊
    3. 功能3:私聊
    4. 功能4:上线消息提醒
    5. 功能5:发送图片
    6. 功能6:新消息提醒

    5.2-页面交互流程图

    通过流程图直观了解业务

    5.3-代码下载

    代码没有做详细优化,后续会更新

    https://gitee.com/lpl666/openliaotianshi.git

  • 相关阅读:
    68
    56
    Django manager 命令笔记
    Django 执行 manage 命令方式
    Django 连接 Mysql (8.0.16) 失败
    Python django 安装 mysqlclient 失败
    H.264 SODB RBSP EBSP的区别
    FFmpeg—— Bitstream Filters 作用
    MySQL 远程连接问题 (Windows Server)
    MySQL 笔记
  • 原文地址:https://www.cnblogs.com/lpl666/p/12986612.html
Copyright © 2011-2022 走看看