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

社区征文

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连接已关闭!');
})

我们将上面的知识点总结实操一下:

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文件。修改如下所示:

//开启promisify
constructor{
  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>

文章来源:为了使远程工作不受影响,我写了一个内部的聊天室 | 社区征文 (infoq.cn)

139
1
0
0
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论