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

  • 相关阅读:
    Django之templates模板
    Django视图函数之request请求与response响应对象
    Django视图函数之三种响应模式
    Django视图函数函数之视图装饰器
    django 获取request请求对象及response响应对象中的各种属性值
    Django 项目中设置缓存
    python 中 使用sys模块 获取运行脚本时在命令行输入的参数
    Mac 设置终端中使用 sublime 打开文件
    iterm2 恢复默认设置
    Python replace方法的使用
  • 原文地址:https://www.cnblogs.com/lpl666/p/12986612.html
Copyright © 2011-2022 走看看