webSocket實現多人聊天功能


webSocket實現多人在線聊天

主要思路如下:

1.使用vue構建簡單的聊天室界面

2.基於nodeJs 的webSocket開啟一個socket后台服務,前端使用H5的webSocket來創建一個socket對象來鏈接后端的socket服務

3.后端開啟一個socket服務后,可以監聽到客戶端的鏈接,以及客戶端發送過來的消息;也可以主動給客戶端發送消息,例如:當有新的客戶端連接的時候,服務端主動給所有連接的客戶發消息,讓所有人都知道有新的用戶加入聊天室

4.前端new一個webSocket實例,去連接socket服務,然后客戶端和服務端就可以實現雙向通訊了,連接到后端服務之后,我們就可以向服務端發送消息了,並且也可以監聽到服務端發送過來的消息。

5.雙向通訊建立起來之后,可以根據功能需求來進行相應的代碼邏輯

 

一、使用vue構建一個簡單的項目,並開發一個簡單的聊天頁面

聊天頁面代碼,前端的主要邏輯都在這里 chating/index.vue

<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">
              <div :key="index" v-for="(item, index) in chatingRecords">
                <li class="other" v-show="item.nickName != myNickName">
                  <img alt="用戶頭像" src="../../assets/logo.png" />
                  <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.png" />
                </li>
              </div>
            </ul>
          </div>
          <div class="chating-btns">
            <input class="input-text" placeholder="請輸入聊天內容" type="text" v-model="text" />
            <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.png" />
              <span>{{item.userName}}</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=', // socket服務地址
      client: null, // webSocket實例
      chatingRecords: [], // 聊天記錄
      myNickName: '', // 是否是自己
      userName: '',
      showLogin: false,
      userList: []
    }
  },
  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服務')
          }
        }

        /* 監聽服務端發送的消息 */
        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實例清除
        }

        /* 監聽服務端異常 */
        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.initChaing()
    },
    loginOutHandler () {
      this.client.close()
      this.client = null // 客戶端或者是服務端斷開后,將webSocket實例清除
      this.$router.push('/')
    },
    sendData () {
      if (!this.myNickName) {
        alert('請登錄')
        this.showLogin = true
        return
      }

      let data = {
        nickName: this.myNickName,
        uid: new Date().getTime(),
        message: this.text,
        date: new Date()
      }
      if (this.client) {
        this.client.send(JSON.stringify(data))
        this.text = ''
      } else {
        console.log('socket服務連接失敗,正在重新連接服務..')
        this.initChaing()
      }
    }
  },
  beforeDestroy () {
    this.client.close()
  }
}
</script>

<style lang="">
.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 {
  max-width: 800px;
  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: purple;
}
.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;
  background: palevioletred;
}
</style>

 

二、使用nodeJs搭建一個webSocket服務

webSocket.js文件

/* 首先在根目錄下安裝nodeJs的ws模塊:npm install ws */

/* 引入nodejs的webSocket模塊 */
var WebSocket = require('ws').Server
var moment = require('moment')

/* 創建一個webSocket實例 */
var wss = new WebSocket({
  url: 'localhost', // webSocket服務的ip
  port: 8888 // webSocket服務的端口
})

/* socketId */
var id = 0
var onlineMemberList = []
var defaultUser = 'user'

/* 監聽客戶端連接 */
wss.on('connection', function (ws, req) {
  id++
  ws.id = 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
  }
  var userInfo = {
    userName: userName,
    socketId: id,
    date: moment().format('MMMM Do YYYY, h:mm:ss a')
  }
  /* 當用戶名一樣的時候,表示重新登錄 */
  for (var 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)
    let 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.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')

為了方便管理代碼,我把webSocket服務的代碼放到了vue項目根目錄下的socketServe文件夾下面,在啟動vue項目之前先要來到這個文件夾里面開啟socket服務,才能在聊天界面連接到服務器

github地址:https://github.com/yanhuomili/chatingGroup

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM