当前位置: 首页 > article >正文

来咯来咯webSocket

在项目总目录下 设置socketServe文件夹 里面创建下面两个文件

使用的时候需要开启 node webSocket.cjs

var { Server } = require('ws');
var moment = require('moment');

const wss = new Server({
  port: 8888
});

let id = 0;
let onlineMemberList = [];
const defaultUser = 'user';

wss.on('connection', function (ws, req) {
  id++;
  ws.id = id;

  let reqUser = req.url.split('?')[1];
  let name = reqUser && reqUser.split('=')[1];
  let userName;

  if (name) {
    userName = decodeURIComponent(name);
  } else {
    userName = defaultUser + id;
  }

  const userInfo = {
    userName: userName,
    socketId: id,
    date: moment().format('MMMM Do YYYY, h:mm:ss a')
  };

  for (let i = 0; i < onlineMemberList.length; i++) {
    if (userInfo.userName === onlineMemberList[i].userName) {
      onlineMemberList[i] = userInfo;
      wss.clients.forEach(itemWs => {
        itemWs.send(JSON.stringify(onlineMemberList));
      });
      return;
    }
  }

  onlineMemberList.push(userInfo);
  wss.clients.forEach(itemWs => {
    itemWs.send(JSON.stringify(onlineMemberList));
  });

  ws.on('message', function (data) {
    console.log(data);
    const newData = JSON.parse(data);
    newData.serveDate = moment().format('MMMM Do YYYY, h:mm:ss a');

    wss.clients.forEach(itemWs => {
      itemWs.send(JSON.stringify(newData));
    });
  });

  ws.on('close', function (ev) {
    console.log('客户端断开连接');
    onlineMemberList = onlineMemberList.filter(item => {
      return item.socketId !== ws.id;
    });

    wss.clients.forEach(itemWs => {
      itemWs.send(JSON.stringify(onlineMemberList));
    });
    console.log(onlineMemberList, 'onlineMemberList');
    console.log(ws.id, 'ws.id');
  });

  ws.on('error', function (ve) {
    console.log('客户端异常');
  });
});

console.log('webSocket服务已开启,端口为:8888');
{
  "type": "module"
}

页面展示 监听消息

<template>
  <div class="chating">
    <div class="chating-wrap">
      <div class="title">聊天页面</div>
      <div class="chating-content">
        <div class="chating-body">
          <div class="chating-list">
            <ul class="chating-records" ref="chatingList">
              <div
                :key="index"
                v-for="(item, index) in chatingRecords"
              >
                <li
                  class="other"
                  v-show="item.nickName != myNickName"
                >
                  <img
                    alt="用户头像"
                    src="@/assets/logo.svg"
                  />
                  <div class="record-text-wrap">
                    <div class="nick-name">{{item.nickName}}</div>
                    <div class="record-text">{{item.message}}</div>
                  </div>
                </li>
                <li
                  class="my"
                  v-show="item.nickName == myNickName"
                >
                  <div class="record-text-wrap">
                    <!-- <div class="nick-name">迷离</div> -->
                    <div class="record-text">{{item.message}}</div>
                  </div>
                  <img
                    alt="用户头像"
                    src="@/assets/logo.svg"
                  />
                </li>
              </div>
            </ul>
          </div>
          <div class="chating-btns">
            <input
              class="input-text"
              placeholder="请输入聊天内容"
              type="text"
              v-model="text"
              @keydown.native.enter="sendData"
            />
            <button
              @click="sendData"
              class="send"
            >发送</button>
          </div>
        </div>
        <div class="chating-online-number">
          <div class="online-num">在线用户{{userList.length}}</div>
          <ul v-if="userList.length > 0">
            <li
              :key="index"
              class="user"
              v-for="(item, index) in userList"
            >
              <img
                alt="用户头像"
                src="@/assets/logo.svg"
              />
              <span>{{ item.userName }}</span>
              <span v-if="userList.includes(item)">(在线)</span>
              <span v-else>(离线)</span>
            </li>
          </ul>
          <button @click="loginOutHandler">退出群聊</button>
        </div>
      </div>
    </div>

    <div
      class="login"
      v-if="showLogin"
    >
      <div class="opacity-wrap">
        <div>
          用户名:
          <input
            class="user-name"
            v-model="userName"
          />
        </div>
        <button
          @click="loginHandler"
          class="login-btn"
        >登录</button>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      text: '',
      socketUrl: 'ws://localhost:8888?userName=',
      client: null,
      chatingRecords: [],
      myNickName: '',
      userName: '',
      showLogin: false,
      userList: [],
      onlineStatus: false // 添加在线状态变量,默认为 false 表示离线
    }
  },

  created() {
    console.log('created')
    // this.initChaing()
  },
  mounted() {
    console.log('mounted')
  },
  methods: {
    /* 初始化聊天,连接socket */
    initChaing() {
      let that = this
      if (window.WebSocket) {
        /* webSocket 连接服务器 */
        this.client = new WebSocket(this.socketUrl + this.myNickName)

        /* 监听客户端连接 */
        this.client.onopen = function (ev) {
          if (ev.type === 'open') {
            console.log('客户端连接socket服务')
            that.onlineStatus = true; // 更新在线状态为 true
          }
        }

        /* 监听服务端发送的消息 */
        this.client.onmessage = function (ev) {
          let data = JSON.parse(ev.data)
          /* 用户在线信息接收的是一个jsony数组 */
          if (data instanceof Array === true) {
            that.userList = data // 在线用户数量变化
          } else {
            /* 聊天信息接收的是一个json对象 */
            that.chatingRecords.push(data) // 在线用户聊天
          }
        }

        /* 监听服务端关闭 */
        this.client.onclose = function (ev) {
          console.log('socket服务已关闭')
          that.client = null // 客户端或者是服务端断开后,将webSocket实例清除
          that.onlineStatus = false; // 更新在线状态为 false
        }

        /* 监听服务端异常 */
        this.client.onerror = function () {
          if (!that.client) {
            console.log('socket服务连接失败')
          }
          that.loginOutHandler()
        }
      } else {
        alert('该浏览器不支持webSocket,请使用主流浏览器,如chrome')
      }
    },
    loginHandler() {
    this.myNickName = this.userName;
    this.showLogin = false;
    this.onlineStatus = true; // 用户登录成功后将在线状态更新为 true
    this.initChaing();
    },

    loginOutHandler() {
      this.client.close()
      this.client = null // 客户端或者是服务端断开后,将webSocket实例清除
      this.$router.push('/')
    },
    
    sendData() {
      if (!this.myNickName) {
        alert('请登录')
        this.showLogin = true
        return
      }
      if(this.text === ''){
        return
      }

      let data = {
        nickName: this.myNickName, // 将 myNickName 改为 nickName
        uid: new Date().getTime(),
        message: this.text,
        date: new Date()
      }
      if (this.client) {
        this.client.send(JSON.stringify(data))
        this.text = ''

        // 在添加新消息后,将列表容器滚动到底部
        this.$nextTick(() => {
          let chatList = this.$refs.chatingList;
          if (chatList) {
            // 计算滚动位置,考虑到边距和边框
            let scrollHeight = chatList.scrollHeight;
            let clientHeight = chatList.clientHeight;
            let offset = 54; // 考虑到可能的额外偏移量
            chatList.scrollTop = scrollHeight - clientHeight + offset;
          }
        });
      } else {
        console.log('socket服务连接失败,正在重新连接服务..')
        this.initChaing()
      }
    }
  },
  beforeDestroy() {
    this.client.close()
  }
}
</script>

<style>
.login {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  justify-content: center;
  align-items: center;
}
.opacity-wrap {
  width: 500px;
  height: 300px;
  background: #fff;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}
.user-name {
  font-size: 16px;
  padding: 5px;
  text-indent: 10px;
}
.login-btn {
  font-size: 20px;
  background: cornflowerblue;
  color: 20px;
  margin-top: 30px;
  color: #fff;
  border: none;
  outline: none;
  padding: 10px 20px;
  border-radius: 10px;
}
ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.chating {
  width: calc(100vw - 200px);
  max-width: 800px;
  max-height: 600px;
  overflow-y: auto;
  border: 20px solid lightcyan;
  border-radius: 20px;
  margin: 0 auto 0;
}
.title {
  background: cornflowerblue;
  color: #fff;
  padding: 5px 0 5px;
}
.chating-content {
  width: 100%;
  display: flex;
  justify-content: space-between;
}
.chating-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  background: #f3f3f3;
}
.chating-list {
  flex: 1;
  border: 1px solid cornflowerblue;
}
.chating-records {
  padding: 10px;
  min-height: 300px;
  max-height: 600px;
  overflow-y: auto;
}
.chating-records li {
  margin-bottom: 20px;
}
.chating-records .other {
  display: flex;
  justify-content: start;
  align-items: flex-start;
}
.chating-records .my {
  display: flex;
  justify-content: flex-end;
  align-items: center;
}

.chating-records img {
  width: 36px;
  height: 36px;
  /* border-radius: 50%; */
  margin-right: 15px;
  background: rgba(228, 157, 228,0.5);
}
.chating-records .my img {
  margin-right: 0;
  margin-left: 15px;
}
.chating-records .other .record-text-wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.chating-records .my .record-text-wrap {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}
.nick-name {
  font-size: 14px;
  margin-bottom: 5px;
  color: #666;
}
.record-text {
  max-width: 260px;
  text-align: left;
  font-size: 14px;
  padding: 5px;
  background: #fff;
  border-radius: 5px;
}

.chating-btns {
  background: burlywood;
  padding: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.input-text {
  font-size: 16px;
  border: none;
  outline: none;
  padding: 5px 0 5px 5px;
}
.send {
  font-size: 16px;
  border: none;
  outline: none;
  padding: 4px 15px;
  margin-left: 20px;
}

.online-num {
  font-size: 12px;
  padding-bottom: 15px;
}
.chating-online-number {
  padding: 15px;
  height: 100%;
}
.chating-online-number ul {
  list-style: none;
  margin: 0;
  padding: 0;
  min-width: 120px;
  max-height: 580px;
  overflow-y: auto;
}
.user {
  display: flex;
  justify-content: space-between;
  align-content: center;
  line-height: 20px;
  font-size: 12px;
  border-bottom: 1px solid aqua;
  padding: 10px;
  margin-bottom: 5px;
}
.user img {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  margin-right: 5px;
}
</style>

http://www.kler.cn/a/384623.html

相关文章:

  • Latex中给公式加边框
  • wireshark工具使用
  • 如何在vscode中安装git详细新手教程
  • HarmonyOS第一课——DevEco Studio的使用
  • 基于C++的决策树C4.5机器学习算法(不调包)
  • oneplus6-build.md
  • JavaEE初阶---servlet篇(二)(smartTomcat的使用相关错误类型)
  • 【智能算法应用】哈里斯鹰算法优化二维栅格路径规划问题
  • CoEdge: 面向自动驾驶的协作式边缘计算系统,实现分布式实时深度学习任务的高效调度与资源优化
  • ruoyi-vue中的mybatis改为mybatis-plus
  • 【数据结构-合法括号字符串】【hard】【拼多多面试题】力扣32. 最长有效括号
  • 阿里云对象存储OSS
  • 恋爱脑学Rust之智能指针Rc,RefCell和Weak指针
  • 重构代码之添加参数
  • [单例模式]
  • 【设计模式系列】桥接模式(十三)
  • LLMs之PDF:zeroX(一款PDF到Markdown 的视觉模型转换工具)的简介、安装和使用方法、案例应用之详细攻略
  • uniapp中使用原生ajax上传文件并携带其他数据,实时展示上传进度
  • 外包干了2年,快要废了。。。
  • [Element] el-table修改滚动条上部分的背景色
  • 科比投篮预测——数据处理与分析
  • ES6的Proxy到底是什么?
  • LINUX下的Mysql:Mysql基础
  • 前后端分离中台管理系统
  • BERT的中文问答系统28
  • Golang | Leetcode Golang题解之第540题有序数组中的单一元素