写点什么

为了工作不受影响,我写了一个内部的聊天室 | 社区征文

作者:云小梦
  • 2022 年 6 月 30 日
  • 本文字数:3541 字

    阅读完需:约 12 分钟

socket 通常也称作“套接字”,用于描述 IP 地址和端口,是一个通信链的句柄。可以用来实现不同虚拟机或不同计算机之间的通信。网络上的两个程序通过一个双线的通信连接实现数据的交换,这个连接的一端称为一个 socket。


WebSocket 是基于 TCP 的一种新的网络协议,它实现了浏览器与服务器全双工通信 —— 允许服务器主动发信息给客户端。和 HTTP 的 Request 请求不同,在实现 websocket 连接的过程中,浏览器需要发出 websocket 连接请求,然后服务器做出回应,这个过程也就是常说的“握手”。


在 websocket API 中,浏览器和服务器只需要做一个握手的动作,然后浏览器和服务器之间就形成了一条快速通道。


websocket 一般用在“客户端和服务器端交互紧密并且极度频繁”的场景下(比如:端对端的聊天和网络游戏)。打通两者之间的数据通路,而不用定时一次次地发起普通 http 请求(轮询)。


//启动一个socket代码(客户端)wx.connectSocket({  //连接一个socket  url:'wss://example.qq.com',  data:{},  header:{    'content-type':'application/json'  },  protocols:['protocol1'],  method:'GET'})
复制代码


表面上看,和普通请求很像,但它的不凡之处就在于:该请求成功连接一个 socket 以后,将会保持这个连接的状态,而普通的 get/post 等请求则是随着 http 的断开而断开。


这时候,可以调用wx.onSocketOpen这个 API 监听 websocket 连接打开事件:


wx.onSocketOpen(function(res){  console.log('WebSocket连接已打开!');})
复制代码


当一个 socket 打开以后,最重要的内容则是通过该 socket 发送一个需要的信息——这需要用到 API:wx.sendSocketMessage;当然,这个“发送”必须在“打开”(的回调 success)之后(WePY 中是在 then 之后):


wx.onSocketOpen(function(res){  wx.sendSocketMessage({    data:msg  })})
复制代码


(不过实际中并不这样写,在页面 Load 中 init“Open”,open 中取 receive,这个 send 反而是放在具体监听的事件中调用)


既然发送出去了,就得接受服务器端的消息(不然怎么“对话”啊~):在打开 socket 之后,可以调用wx.onSocketMessage API 来接收服务器的消息事件


wx.onSocketMessage(function(res){  console.log('收到服务器的消息:'+res.data)})
复制代码


而在消息的发送和接收过程中,因为某些原因出现一些错误是不可避免的——比如客户端设备无法打开 socket、或者网络掉线/延迟、或者服务端请求过多造成拥堵...这时就需要“手动”提示开发者或用户了:


wx.onSocketError(function(res){  console.log('websocket连接打开失败,请检查系统及网络!');})
复制代码


最后,我们完成了一个 socket 连接,用户却不用了,那就要及时断开 —— 一个服务器接收和承载连接数是有限的,及时地断开不需要的链接可以极大地减轻服务器的压力,减少资源的浪费:


wx.onSocketClose(function(res){  console.log('websocket连接已关闭!');})
复制代码




我们将上面的知识点总结一下:实战:简单的 socket 聊天室小程序后端:node.js


npm install -g ws
复制代码


全局安装 websocket 用到的 npm 包。


安装完成后,在项目中新建一个 server.js 文件:


const WebsocketServer=require('ws').Server;let wbsocketServer=new WebsocketServer({  port:8081,  autoAcceptConnections:true})
let clients=[]let connectNum=0//监听连接和消息wbsocketServer.on('connection',(ws)=>{ clients.push(ws); ++connectNum; console.log('连接的数量:'+connectNum);
ws.on('message',(message)=>{ let objMessage=JSON.parse(message); console.log(objMessage.data); //可以做一些处理或者转发其它客户端 }) //随机发送消息 setInterval(()=>{ if(connectNum!==0){ setTimeout(()=>{ //从连接池中获取最新连接 clients[clients.length-1].send(JSON.stringify({data:'来自服务器的消息'})) },Math.random()*1000*3) }else{ console.log('无客户端连接') } },10000) ws.on('close',()=>{ console.log('有连接断开'); //删除不需要的连接——一般是“最老的”一条数据 clients.pop(); --connectNum; })})
复制代码


完后就可以用nodemon server.js命令启动服务器。


小程序中开发时一定要勾选“未校验合法域名...”这一项


客户端开发——WePY


npm install wepy-cli -g
wepy init standard chat--创建了一个chat项目,完成基本配置后,进入该目录
npm i
--生成、监控小程序wepy build -watch
复制代码


在 app.wpy 文件的 config 配置中新增一个 chat 页面,并且开启 promisify,并且在 pages 文件夹下创建 chat.wpy 文件。修改如下所示:


//开启promisifyconstructor{  super();  this.use('requestfix');  this.use('promisify');}pages:[  'pages/chat'],
复制代码


同一般的微信小程序的<block></block>,我们可以用一个数组存储对话,而使用<repeat></repeat>循环显示聊天内容。chat.wpy -> template


<template>  <view class="page">    <view class="chats">      <repeat for="{{chats}}" item="item">        <view style="font-size:20rpx;color:#ababab">{{item.item}}</view>        <view style="font-size:25rpx;padding-bottom:20rpx;">{{item.text}}</view>      </repeat>    </view>    <view class="chatInput">      <input placeholder="请输入聊天内容" bindinput="userSay" />    </view>    <button @tap="sendMessage" size="mini" class="btn">发送消息</button>  </view></template>
复制代码


chat.wpy -> style


<style lang="less">  .page{    position:fixed;    height:100vh;    width:100vw;    background:#e8e9d2;  }  .chats{    text-align:center;    margin:10vh 10vh 10vw 10vw;    height:65vh;    width:80vw;    background-color:aliceblue;    overflow:auto;  }  .chatInput{    background:aliceblue;    height:40rpx;    font-size:20rpx;    padding:10rpx;    width:70vw;    margin-left:15vw;    border-radius:20rpx;    margin-bottom:3vh;  }  .btn{    width:70vw;    mnargin-left:15vw;  }</style>
复制代码


chat.wpy -> js/script


<script>  import wepy from 'wepy'  //监听是否打开的状态量  let socketOpen=false  export default class chat extends wepy.page{   //wepy中的固定格式    data={      say:'',      chats:[        {time:'聊天开始',text:''}      ]    }    methods={      //用户输入相关      userSay(e){        this.say=e.detail.value        this.$apply()      },      //发送对话      sendMessage(){        let time=new Date()        this.chats=this.chats.concat([time:time.toLocaleTimeString(),text:'我说:'+this.say])        this.handleSendMessage()        this.$apply()      }    }    //启动一个socket    startSocket(){      wepy.connectSocket({        url:'ws://127.0.0.1:8081'      })    }    wssInit(){      const that=this      this.startSocket()      //连接失败显示      wepy.onSocketError(function(res){        socketOpen=false        console.log('websocket连接打开失败,请检查!',res)        setTimeout(()=>{          that.startSocket()        },2000)      })      //监听连接成功      wepy.onSocketOpen(function(res){        socketOpen=true        console.log('websocket连接已打开')        //接收服务器的消息        that.receiveMessage()      })    }    receiveMessage(){      const that=this      if(socketOpen){        console.log('读取socket服务器...')        wepy.onSocketMessage(function(res){          let time=new Date()          let resData=JSON.parse(res.data)          if(resData.data){            that.chats=that.chats.concat([{time:time.toLocaleTimeString(),text:'服务器说:'+resData.data}])            that.$apply()          }        })      }else{        //未打开状态需要延时重新连接        console.log('服务器没有连接上')        setTimeout(()=>{          that.receiveMessage()        },2000)      }    }    //发送消息    handleSendMessage(){      const that=this      wepy.sendSocketMessage({        data:JSON.stringify({data:that.say})      })    }    events={}
onLoad(){ const that=this setTimeout(()=>{ that.wssInit() },1000) } }</script>
复制代码


发布于: 刚刚阅读数: 3
用户头像

云小梦

关注

求知若渴,虚心若愚 2019.05.11 加入

江湖人称“云小梦”。自号“恪愚”。一个大前端路上还未“毕业”的“小学生”。爱好分享、执着探索、乐于开源;着迷于vue、node、css、前端智能化以及原生js技术。

评论

发布
暂无评论
为了工作不受影响,我写了一个内部的聊天室 | 社区征文_微信小程序_云小梦_InfoQ写作社区