websocket以及ajax轮询★

16 篇文章 0 订阅
5 篇文章 0 订阅

ajax轮询是这个意思

// 客户端一发送小心给服务器,服务器接收信息,发送给客户端二,客户端二是每隔固定的时间去询问服务端有没有客户端 二的消息,这样很消耗性能
// ajax轮询不是实时的,而webSocket是建立长连接,一次连接实时更新信息,没有断连的情况,所以ajax轮询非常消耗性能
// http能不能实现聊天室的效果?
// http基于 请求 ---->响应
// 因为http请求给后端发送一个请求,然后会响应给发送请求者,
// 这个响应信息无法发送给另一个人所以http无法做到聊天室的功能

websocket介绍

// WebSocket协议是基于TCP的一种新的网络协议.它实现了浏览器与服务器全双工通信

// 全双工通信的意思就是:允许客户端给服务器主动发送信息,也支持服务端给另一个客户端发送信息.
websocket属于长连接的一种持久协议
// websocket是一种持久协议,http是非持久协议.
// 现在很多网站都有实时推送的需求,比如聊天室,客服咨询等等
// 早期没有websocket的时候,一般通过ajax轮询,由于http请求,服务器无法给浏览器主动发送数据,
// 因此需要浏览器定时的给服务器发送请求(比如1s一次),服务器把最新的数据响应给浏览器.这种模式的缺点就是浪费性能和资源

WebSocket比较不好的地方就是纯原生实现聊天室

// WebSocket的缺点,没有一个广播的事件或者说方法
// 需要我们自己去封装一个,封装一个广播事件的方法broadcast函数,并且broadcast函数内部职能发送字符串类型
// 如果你发送别的对象数组等等服务端就会报错,所以我们还需要用到JSON.stringify去转换一下发给前端,并且前端还需要哦转
// WebSocket的事件非常的少,用来用去就那几个事件方法

// 所以我们更多的是用一个框架去开发聊天室类的网站
// 我们使用socket.io这个框架去开发

// 我们基于socket.io开发一个完整的聊天室

下面就是我在使用原生的websocket的demo

index.html文件代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>websocket</title>
</head>
<style>
    div {
        width: 200px;
        height: 200px;
        border: 1px solid #000;
    }
</style>

<body>
    <div>npm install nodejs-websocket</div>
    <!-- 输入内容 -->
    <input type="text" placeholder="请输入您的内容">
    <!-- 发送请求的 -->
    <button>发送消息</button>
    <!-- 显示结果 -->
    <div class="d1"></div>
</body>
<script>
    var TYPE_ENTER = 0
    var TYPE_LEAVE = 1
    var TYPE_MSG = 2
    var input = document.querySelector("input")
    var button = document.querySelector("button")
    var d1 = document.querySelector(".d1")
    // 演示websocket在浏览器端如何使用
    // H5已经直接提供了webSocket的API,所以我们可以直接去使用
    // 创建websocket
    // 1.参数一为websocket的服务地址,ws://localhost:6060
    // 参数一不能写http://localhost:6060
    var socket = new WebSocket("ws://localhost:6060")
    // 2.open事件当websocket连接成功的时候触发
    socket.addEventListener("open", function () {
        d1.innerHTML = "连接服务已经成功了!"
    })

    // 3.主动的给websocket服务端发送消息
    // socket.send(value)这个方法把value发送给服务器
    // 在Network里面有个WS里面有headers和messages就可以看到请求的信息
    // messages里面向上的绿色箭头表示我们前端发送给服务器的内容,向下的橙色箭头表示服务端响应给前端的内容
    button.addEventListener("click", function () {
        var value = input.value
        // socket.send(value)这个方法把value发送给服务器
        socket.send(value)
        input.value = ""
    })

    // 4.接收websocket服务的数据
    socket.addEventListener("message", function (e) {
        console.log(e)
        console.log("前端接收到后端响应回来的信息", e.data)
        var dv = document.createElement("li")
        dv.innerHTML = e.data
        d1.appendChild(dv)
    })

    // 5.断开连接
    socket.addEventListener("close", function () {
        alert("与服务器断开连接!")
    })
</script>

</html>

下面是文件夹位置
在这里插入图片描述

服务器的代码

fuwu.js

// 导入node-websocket包
var ws = require("nodejs-websocket")
var TYPE_ENTER=0
var TYPE_LEAVE=1
var TYPE_MSG=2
// 如何处理用户的请求
// 每次只要有用户连接,函数就会被执行,会给当前连接的用户创建一个connect对象
var count = 0
var server = ws.createServer(connect => {
    console.log("有用户连接上来了!")
    count++
    connect.userName = `用户${count}`
    // 有用户连接进来了,告诉所有用户,有人进来了聊天室
    broadcast({
        type:TYPE_ENTER,
        msg:`${connect.userName}进入聊天室`,
        time:new Date().toLocaleDateString()
    })

    // 监听用户发过来的数据使用connet.on事件
    connect.on("text", data => {
        console.log("接收到用户传给服务端的数据", data)
        // // 给用户一个响应的数据
        // connect.send(data.toUpperCase() + "!!!!!")
        // 给所有的用户广播客户发送过来的消息
        broadcast({
            type:TYPE_MSG,
            msg:data,
            time:new Date().toLocaleDateString()
        })
    })
    // 只要websocket连接断开,close事件就会触发
    connect.on("close", () => {
        count--
        console.log("服务器断开连接!")
        // 用户断连,告诉所有的用户,有人离开了聊天室
        broadcast({
            type:TYPE_LEAVE,
            msg:`${connect.userName}离开了聊天室`,
            time:new Date().toLocaleDateString()
        })
    })
    // 注意一定要有error事件不然只有close事件就会报错dos
    // 注册一个error,事件,处理用户的错误信息,用户发生异常!
    connect.on("error", () => {
        console.log('用户连接异常!')
    })
})

// 广播,给所有的用户发送消息
function broadcast(msg) {
    // server.connections:表示所有的用户
    server.connections.forEach((item) => {
        // 注意广播的时候不允许传一个对象只允许传一个JSON字符串
        item.send(JSON.stringify(msg))
        // 这儿是一个坑注意注意
    })
}
server.listen(6060, () => {
    console.log("服务器端口号6060已经成功开启!", "佛祖保佑无我写的代码一切安好无bug");
})

// WebSocket比较不好的地方就是纯原生实现聊天室
// WebSocket的缺点,没有一个广播的事件或者说方法
// 需要我们自己去封装一个,封装一个广播事件的方法broadcast函数,并且broadcast函数内部职能发送字符串类型
// 如果你发送别的对象数组等等服务端就会报错,所以我们还需要用到JSON.stringify去转换一下发给前端,并且前端还需要哦转
// WebSocket的事件非常的少,用来用去就那几个事件方法

// 所以我们更多的是用一个框架去开发聊天室类的网站
// 我们使用socket.io这个框架去开发

// 我们基于socket.io开发一个完整的聊天室
io.emit可以用来广播不需要封装函数对传输内容也没有限制
    // 用户断开连接的功能
    // 监听用户断开连接
    // 每个用户都有连接,如果浏览器断联就会自动触发disconnect事件
    // 前端不需要写emit disconnect事件
    socket.on("disconnect", function (data) {
        // 把当前用户的信息从users中删除
        var idx = users.findIndex(item => item.username === socket.username)
        // 删除掉断开连接的人
        users.splice(idx, 1)
        // 1.告诉所有人,有人离开了聊天室
        io.emit("delUser",{
            username:socket.username,
            avatar:socket.avatar
        })
        // 2.告诉所有人,userList发生更新
        io.emit("userList",users)
    })

我们基于socket.io做出来的demo聊天室

登录页面的demo样例图片在这里插入图片描述

聊天的页面

在这里插入图片描述

为了更加的简单便捷的去开发一个websocket项目我们使用到了socket.io去开发

在这里插入图片描述
我们基于socket.io开发的demo文件夹层级关系
server.js

var express = require("express")
var app = express()
var server = require("http").Server(app)
var io = require("socket.io")(server)
// 记录已经登录过的用户
var users = []
app.use(express.static("./public"))
server.listen(8083, () => {
    console.log("服务器8083开启成功~!")
})

app.get("/", function (req, res) {
    res.redirect("/index.html")
})

io.on("connection", function (socket) {
    // 用户连接的功能
    socket.on("login", function (data) {
        //    判断,如果data在user中存在,说明该用户已经登录了不允许登录
        // 如果data在users中不存在,说明该用户没有登录,允许用户登录
        var user = users.find(item => item.username === data.username)
        if (user) {
            // 表示用户存在,登录失败!服务器需要给当前用户响应,告诉登录失败
            socket.emit("loginError", {
                msg: "登陆失败"
            })
            console.log("登录失败!")
        } else {
            // 表示用户不存在,登录成功!
            users.push(data)
            socket.emit("loginSuccess", data)
            console.log("登录成功!")
            // socket.io广播消息如下
            // socket.emit是告诉单个人的事件
            // io.emit是广播给所有用户的事件
            // 告诉所有的用户.目前聊天室谁进入聊天室
            io.emit("addUser", data)
            // 告诉所有的用户.目前聊天室中有多少人
            io.emit("userList", users)
            // 把登录成功的用户名和头像存储起来
            socket.username = data.username
            socket.avatar = data.avatar
        }
    })

    // 用户断开连接的功能
    // 监听用户断开连接
    // 每个用户都有连接,如果浏览器断联就会自动触发disconnect事件
    // 前端不需要写emit disconnect事件
    socket.on("disconnect", function (data) {
        // 把当前用户的信息从users中删除
        var idx = users.findIndex(item => item.username === socket.username)
        // 删除掉断开连接的人
        users.splice(idx, 1)
        // 1.告诉所有人,有人离开了聊天室
        io.emit("delUser",{
            username:socket.username,
            avatar:socket.avatar
        })
        // 2.告诉所有人,userList发生更新
        io.emit("userList",users)
    })

    // 监听聊天的消息
    socket.on("sendMessage",data=>{
        console.log(data)
        // 广播给所有的用户
        io.emit("receiveMessage",data)
    })


    //接收图片信息
    socket.on("sendImage",data=>{
        console.log(data)
        // 广播给所有的用户
        io.emit("receiveImage",data)
    })
})

index.js

/* global io */
/* global $ */
/* 聊天室的主要功能 */

// 1.连接socketio服务
var socket = io('http://localhost:8083')
var username, avatar
var toName = '群聊'

// 2.登录功能
$('#login_avatar li').on('click', function () {
  $(this).addClass('now').siblings().removeClass('now')
})
// 点击按钮,登录
$('#loginBtn').on('click', function () {
  // 获取用户名
  var username = $('#username').val().trim()
  if (!username) {
    window.alert('请输入用户名')
    return
  }
  if (username === '群聊') {
    window.alert('用户名已存在')
    return
  }
  // 获取选择的头像
  var avatar = $('#login_avatar li.now img').attr('src')
  console.log(username, avatar)
  // 需要告诉socket io服务,登录
  // 前端携带username和avatar去触发后端的login事件
  socket.emit('login', {
    username,
    avatar
  })
})
// 监听登录失败的请求
socket.on('loginError', data => {
  window.alert('用户名已存在')
})

// 监听登录成功的请求
socket.on('loginSuccess', data => {
  console.log('登录成功')
  // 登录成功
  // 隐藏登录窗口
  // $('.login_box').fadeOut()
  $('.login_box').hide()
  // 显示聊天窗口
  $('.container').fadeIn()
  // 设置个人信息
  $('.user-list .header img').attr('src', data.avatar)
  $('.user-list .header .username').text(data.username)

  username = data.username
  avatar = data.avatar
})

// 监听添加用户的消息
socket.on('addUser', data => {
  // 添加一条系统消息
  $('.box-bd').append(`
    <div class="system">
      <p class="message_system">
        <span class="content">${data.username} 加入群聊</span>
      </p>
    </div>
  `)
  scrollIntoView()
})

// 监听用户离开的消息
socket.on('delUser', data => {
  // 添加一条系统消息
  $('.box-bd').append(`
    <div class="system leave">
      <p class="message_system">
        <span class="content">${data.username} 离开了群聊</span>
      </p>
    </div>
  `)
  scrollIntoView()
})

// 监听用户列表的消息
socket.on('userList', data => {
  // 把userlist
  $('.user-list ul').html('')
  $('.user-list ul').append(`
    <li class="user">
      <div class="avatar"><img src="images/群聊.jpg" alt=""></div>
      <div class="name">群聊</div>
    </li>
  `)
  data.forEach(item => {
    $('.user-list ul').append(`
      <li class="user">
        <div class="avatar"><img src="${item.avatar}" alt=""></div>
        <div class="name">${item.username}</div>
      </li>
    `)
  })
  $('#userCount').text(data.length)
  clickUser()
})

// 聊天功能
$('#btn-send').on('click', () => {
  var content = $('#content').html().trim()
  $('#content').html('')
  if (!content) {
    return window.alert('请输入内容')
  }
  // 发送消息给服务器
  socket.emit('sendMessage', {
    msg: content,
    username,
    avatar
  })
})

// 监听接收聊天消息
socket.on('receiveMessage', data => {
  console.log('收掉消息', data)
  if (data.toName === '群聊') {
    if (username === data.username) {
      // 自己的消息
      $('.box-bd').append(`
        <div class="message-box">
          <div class="my message">
            <img src="${data.avatar}" alt="" class="avatar">
            <div class="content">
              <div class="bubble">
                <div class="bubble_cont">${data.msg}</div>
              </div>
            </div>
          </div>
        </div>
      `)
    } else {
      // 别人的消息
      $('.box-bd').append(`
        <div class="message-box">
          <div class="other message">
            <img src="${data.avatar}" alt="" class="avatar">
            <div class="nickname">${data.username}</div>
            <div class="content">
              <div class="bubble">
                <div class="bubble_cont">${data.msg}</div>
              </div>
            </div>
          </div>
        </div>
      `)
    }
  } else {
    if (username === data.username) {
      // 自己的消息
      $('.box-bd').append(`
        <div class="message-box">
          <div class="my message">
            <img src="${data.avatar}" alt="" class="avatar">
            <div class="content">
              <div class="bubble">
                <div class="bubble_cont">${data.msg}</div>
                <div class="bubble_toName">私聊</div>
              </div>
            </div>
          </div>
        </div>
      `)
    } else {
      // 别人的消息
      $('.box-bd').append(`
        <div class="message-box">
          <div class="other message">
            <img src="${data.avatar}" alt="" class="avatar">
            <div class="nickname">${data.username}</div>
            <div class="content">
              <div class="bubble">
                <div class="bubble_cont">${data.msg}</div>
                <div class="bubble_toName">私聊</div>
              </div>
            </div>
          </div>
        </div>
      `)
    }
  }
  scrollIntoView()
})

// 当有消息时,将滑动到底部
function scrollIntoView () {
  // 当前元素的底部滚动到可视区
  $('.box-bd').children(':last').get(0).scrollIntoView(false)
}

// 发送图片功能
$('#file').on('change', function () {
  var file = this.files[0]
  // // 需要把这个图片发送到服务器,借助于H5新增的fileReader
  var fr = new window.FileReader()
  fr.readAsDataURL(file)
  fr.onload = function () {
    socket.emit('sendImage', {
      username,
      avatar,
      img: fr.result,
      toName
    })
  }
})

// 监听接收图片消息
socket.on('receiveImage', data => {
  if (username === data.username) {
    // 自己的消息
    $('.box-bd').append(`
      <div class="message-box">
        <div class="my message">
          <img src="${data.avatar}" alt="" class="avatar">
          <div class="content">
            <div class="bubble">
              <div class="bubble_cont">
                <img src="${data.img}">
              </div>
            </div>
          </div>
        </div>
      </div>
    `)
  } else {
    // 别人的消息
    $('.box-bd').append(`
      <div class="message-box">
        <div class="other message">
          <img src="${data.avatar}" alt="" class="avatar">
          <div class="nickname">${data.username}</div>
          <div class="content">
            <div class="bubble">
              <div class="bubble_cont">
                <img src="${data.img}">
              </div>
            </div>
          </div>
        </div>
      </div>
    `)
  }
  // 等待图片加载完成
  $('.box-bd img:last').on('load', function () {
    scrollIntoView()
  })
})

// 初始化jquery-emoji插件
$('.face').on('click', function () {
  // 点击.face的时候再去初始化
  $('#content').emoji({
    // 设置触发表情包的表情按钮
    button: '.face',
    // 表示只有一组标签的时候显不显示tab
    showTab: true,
    animation: 'slide',
    position: 'topRight',
    icons: [{
      name: 'QQ表情',
      path: 'lib/jquery-emoji/img/qq/',
      maxNum: 91,
      excludeNums: [41, 45, 54],
      file: '.gif'
    }]
  })
})

// // 扩展:私聊功能
// $('#btn-send').on('click', () => {
//   var content = $('#content').html().trim()
//   console.log(content)
//   if (!content) {
//     return window.alert('请输入内容')
//   }
//   if (toName === '群聊') {
//   // 发送消息给服务器
//     socket.emit('sendMessage', {
//       msg: content,
//       username,
//       avatar,
//       toName
//     })
//   } else {
//     // 发送私聊消息给服务器
//     socket.emit('sendMessageToOne', {
//       msg: content,
//       username,
//       avatar,
//       toName: toName
//     })
//   }
//   $('#content').html('')
// })

// 点击用户事件绑定
function clickUser () {
  $('.user').on('click', function () {
    $(this).addClass('active').siblings().removeClass('active')
    var to = $(this).children('.name').text()
    $('#chatName').text(to)
    toName = to
  })
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值