nodejs利用websocket搭建一个聊天室

源码

搭建服务器

搭建一个返回网页的端口

const express = require('express')
const app = express()
const path = require('path')
const fs = require('fs')
app.use(express.static(path.join(__dirname, 'public')))
//监听端口
app.listen(8080,()=>{
  console.log('HTTP Server is running on: http://localhost:%s', 8080)
})
app.get('*',(req,res)=>{
  let urlPath = req.path
  if(urlPath == '/'||urlPath=='/index.html'||urlPath=='/index'||urlPath=='') {
    console.log('返回index.html')
    res.end(fs.readFileSync(`./index.html`))

  } 
})

搭建一个监听websocket的端口

安装插件

npm install ws
var WebSocketServer = require('ws').Server,
wss = new WebSocketServer({ port: 8181 });
wss.on('connection', function (ws) {
  console.log('client connected');
  ws.on('message', function (message) {
    console.log(message);
  });
});

创建一个html文件

<!DOCTYPE html>
<html>
  <meta charset="utf-8">
  <head><title>websoket</title></head>
  <body>
    websoket
    <input type="text" id='message'>
    <button >发送</button>
  </body>
  <script>
   var ws = new WebSocket("ws://localhost:8181");
    ws.onopen = function (e) {
      console.log('Connection to server opened');
    }
    document.querySelector('button').addEventListener('click',sendMessage)
    function sendMessage() {
      console.log(document.querySelector('#message').value)
      // 发送
      ws.send(document.querySelector('#message').value)
    }
  </script>
</html>

运行结果

在这里插入图片描述
在这里插入图片描述
数据成功发送到服务器。

websocket

参考资料阮一峰websocket
这里就摘要的讲下websocket。
HTTP请求一般只能用户发起,如果服务器有信息想要主动发送给用户的时候,单靠HTTP是不行的。
这就引出了维持通信的必要性,客户端和服务器建立了一个通信通道(websocket),双方都可以主动的发送数据给对方。

应用场景:股票市场、聊天软件

使用到的网络协议

  1. HTTP一样,同样是经过TCP连接建立起来的面向连接。
  2. 握手阶段使用的和http协议是一样
  3. 数据格式比较轻量,性能开销小,通信高效。
  4. 可以发送文本,也可以发送二进制数据。
  5. 没有同源限制,客户端可以与任意服务器通信。实现跨域
  6. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。有点类似HTTPHTTPS的区别

在这里插入图片描述

服务器如何写一个websocket监听端口

从上面的一个例子非常清晰的明白,使用了ws框架可以快速地搭建一个websocket的服务器。

var WebSocketServer = require('ws').Server,// 引入框架
wss = new WebSocketServer({ port: 8181 });// 设置端口号
wss.on('connection', function (ws) { // 注册连接事件
  console.log('client connected');// 成功建立之后执行
  ws.on('message', function (message) {// 注册监听事件
    console.log(message);// 当收到客户端发送的信息执行
  });
});

这里有官方文档

利用websocket实现一个聊天室

聊天室需要一个用户表,还有聊天框。
也需要一些按钮来控制发送和注册用户等

<!DOCTYPE html>
<html>
  <meta charset="utf-8">
  <head><title>websoket</title></head>
  <style>
   input{
    outline-style: none ;
    border: 1px solid #ccc; 
    border-radius: 3px;
    padding: 13px 14px;
    width: 620px;
    font-size: 14px;
    font-weight: 700;
    font-family: "Microsoft soft";
    margin: 20px;
  }
  input:focus{
    border-color: #66afe9;
    outline: 0;
    -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
    box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)
  }
  span{
    font-size: 24px;

    font-weight: 700;
    font-family: "Microsoft soft";
  }
  button {  
        width: 120px;  
        padding:8px;  
        margin: 10px;
        background-color: #428bca;  
        border-color: #357ebd;  
        color: #fff;  
        -moz-border-radius: 10px;  
        -webkit-border-radius: 10px;  
        border-radius: 10px; /* future proofing */  
        -khtml-border-radius: 10px; /* for old Konqueror browsers */  
        text-align: center;  
        vertical-align: middle;  
        border: 1px solid transparent;  
        font-weight: 900;  
        font-size:100%  
      }  

  </style>
  <body>
    <span>username</span> 
    <input type="text" id='username' placeholder="请输入名称">
    <button id='confirm'>确认</button>
    <br>
  <div id='talk' style='margin:20px;width:600px;height:400px;border:1px solid black;'>
    </div>
    <br>
    <input type="text" id='message'><br>
    <button id='sendBtn'>发送</button><br>
    <button id='closeWs'>退出聊天室</button>
    <div id='users' style="position: fixed;right:50px;top:20px;width:200px;height:200px;background: #00CCFF;">
      <li>123</li>
    </div>
  </body>
</html>

在这里插入图片描述

逻辑处理

建立连接

  • 客户端
// 建立一个WS连接
    var ws = new WebSocket("ws://localhost:8181");
        // 打开连接
    ws.onopen = function (e) {
      console.log('Connection to server opened');
    }
    // 关闭连接 删除用户
    ws.onclose = function (e) {
      console.log('Connection to server closed');
    }
  • 服务端
var WebSocketServer = require('ws').Server
var wss = new WebSocketServer({ port: 8181 });
wss.on('connection', function (ws) {
  console.log('client connected');
  ws.on('message', function (message) {
    console.log(JSON.parse(message));
});

信息约定

由于http是无状态的,无法自动判断信息的类型,所以我们这里约定一个信息约定。

  • 每次客户端发送信息都会携带一个状态码:
    • 1代表注册信息,
    • 2代表修改用户信息,
    • 3代表发送聊天信息,
    • 4代表注销信息。

用户端同理,都会携带一个状态码提供用户进行判断信息类型。

添加注册用户事件

  • 客户端
    // 接受服务器信息
    ws.onmessage= function(message) {
      console.log('服务器返回的信息')
      let state = message.data.slice(0,1)
      let data = JSON.parse(message.data.slice(1))
      if(state==1) {// 表示用户信息更新
        updateUserName(data)
      }
      console.log(state,data)
    }
    // 更新用户表
    function updateUserName(data) {
      var parent = document.querySelector('#users')
      parent.innerHTML =''
      data.forEach((item)=>{
        var list = document.createElement('li')
        list.innerHTML = item.name
        parent.appendChild(list)
      })
    }
    // 添加用户
    document.querySelector('#confirm').addEventListener('click',()=>{
      username = document.querySelector('#username').value
      if(flag) return 
      // 修改信息,并且发送信息到服务器
      let mess = {
          id:userId,
          name:username,
          state:1
        }
        flag = true
        ws.send(JSON.stringify(mess))
    })
    
  • 服务端
 
// 保存用户id
var users = []
var wss = new WebSocketServer({ port: 8181 });
wss.on('connection', function (ws) {
  console.log('client connected');
  ws.on('message', function (message) {
    message = JSON.parse(message)
    // 判断状态
    if(message.state==1) {
      // 注册用户时间
      users.push({
        id:message.id,
        name:message.name
      })
      // 将更新的用户信息发送给所有用户
      updateAllUsername()
    }
  });
});
function updateAllUsername () {
  for(let item of wss.clients){
    console.log(item.domain)
    item.send(`1${users}`)
  }
}

我们知道,wss是一个websocket的server实例,注册了一个监听端口,当一个用户连接这个端口的时候,就会触发里面的监听事件。
connection表示连接建立完成,后面回调函数中的参数则是请求头
nodejs的ws框架有一个数据保存着所有的请求头,它就是wssserver实例的clients,我们可以遍历它,就可以完成当一个用户进入聊天室时候,让所有用户更新信息。

注销用户信息

当用户退出聊天室,也需要更新用户信息表。

  • 客户端
    // 关闭连接事件
    document.querySelector('#closeWs').addEventListener('click',()=>{
   		// 取消用户信息
      // 修改信息,并且发送信息到服务器
        let mess = {
          id:userId,
          state:4
        }
        ws.close(3000,JSON.stringify(mess);
    })
  • 服务器端
wss.on('connection', function (ws) {
  console.log('client connected');
/***/
// 给每一个用户注册一个关闭事件
  ws.on('close',(state,msg)=>{
    // 通过id删除用户表
    let id = JSON.parse(msg).id
    users= users.filter((item)=>{
      return item.id!==id
    })
    // 给所有还在用的用户更新列表
    updateAllUsername()
  })
});

我们来测试下
用户1:在这里插入图片描述
用户2:在这里插入图片描述
可以看到用户2 成功添加到用户列表,并且切会到用户1发现用户列表也更新了。

  • 测试退出情况
    用户1退出,查看用户2情况。
    在这里插入图片描述

聊天内容

有了前面的基础,这部分已经非常简单了。

  • 客户端
    // 发送聊天信息
    document.querySelector('#sendBtn').addEventListener('click',() => {// 发送
      if(!flag){
         alert('请注册用户')
        return 
      }
      if(document.querySelector('#message').value!=''&&flag) {
        // 拼接数据
        let mess = {
          id:userId,
          name:username,
          state:3,
          time:new Date().toLocaleDateString(),
          value:document.querySelector('#message').value
        }
        ws.send(JSON.stringify(mess))
        document.querySelector('#message').value=''
      }
    })
    // 接受聊天信息
    ws.onmessage= function(message) {
      console.log('服务器返回的信息',message.data)
      let state = message.data.slice(0,1)
      let data = JSON.parse(message.data.slice(1))
      if(state==1||state==4) {
        updateUserName(data)
      }else if (state == 3) {
        updateTalkMessage(data)
      }
    }
    // 更新聊天框信息
    function  updateTalkMessage(data) {
      console.log(data)
      var parent = document.querySelector('#talk')
      var list = document.createElement('li')
      list.innerHTML = `${data.name}  说: ${data.value}  -----${data.time}`
      parent.appendChild(list)
    }
  • 服务器
wss.on('connection', function (ws) {
 console.log('client connected');
  ws.on('message', function (message) {
    message = JSON.parse(message)
    console.log(message)
    // 判断状态
    if(message.state==1) {
      // 注册用户时间
      users.push({
        id:message.id,
        name:message.name
      })

    }else if(message.state==3) {
      talkMessage = {
        name:message.name,
        time:message.time,
        value: message.value
      }
    }
    
    // 将更新的信息发送给所有用户
    updateAllUsername(message.state)
  });
})
function updateAllUsername (state) {
  let result = ''
  if(state== 1||state==4) {
    result=users
  } else if (state == 3 ) {
    result = talkMessage
  } 
  for(let item of wss.clients){
    item.send(`${state}`+JSON.stringify(result))
  }
}

查看演示
在这里插入图片描述

  • 还剩下一个更新用户信息,相信看到现在应该可以独立完成了,和前面的功能差不多,就是获取数据、处理数据和返回数据。

总结

有了websocket这个接口方便了用户端和服务器持续的交流,比起使用http轮询地访问大大提高了性能。
websocket这个api使用起来非常简单,相信你如果能够自己搭一个聊天室,一定就掌握了七七八八了。

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值