websocket以及聊天室的实现

本文介绍了WebSocket协议,包括其长连接的特点和优势,并展示了如何在Web应用中实现聊天室功能。通过WebSocket,服务端可以实时向客户端推送消息,实现双向通信。文中提供了服务端和客户端的代码示例,详细讲解了连接建立、消息收发以及群聊和私聊的实现方法。同时,讨论了在线人数实时更新和登录业务的处理。
摘要由CSDN通过智能技术生成

HTTP keep-alive参考博文
socketio跨域 socketio跨域问题自己去了解

WebSocket

websocket可以实现客户端与服务端之间的数据实时通信。(长连接)

网络通信过程中的长连接与短连接

短连接: 客户端发起连接请求,请求建立连接,服务端接受请求,完成连接的创建,而后客户端通过该连接发送数据,服务端处理请求,返回响应,最后连接断开。这种通讯模式称为短连接。

特点:这种通讯模式将会及时的释放服务端资源,所以可以为更多的客户端提供服务。但是也有缺点:无状态。(每个请求都是独立的,无法保存客户端状态)

**长连接:**客户端发起连接请求,请求建立连接,服务端接受请求,完成连接的创建,而后客户端通过该连接发送数据,服务端处理请求,返回响应;服务端也可以主动的向客户端发送数据,客户端接收。这个过程中持续连接不断开。这种通讯模式称为长连接。

特点:支持客户端与服务端之间的实时通信。服务端可以随时随地向客户端发消息。缺点就是浪费服务端资源。

什么是websocket

websocket是一种在单TCP连接上进行的支持全双工通信(通信的过程中允许两个方向上同时完成数据传输)的一种协议。是一款长连接协议。

如何在网页中建立websocket连接?

服务端接收接收该连接?

客户端与服务端如何通过该websocket连接进行通信呢?

socket.io

https://socketio.bootcss.com/

socket.io是一个为浏览器与服务端之间提供实时的、双向的、基于事件通信的网络通信库。底层封装了websocket协议。提供了一些简单的API接口,方便的建立连接,收发消息,抹平了一些技术细节与浏览器兼容性问题。

建立连接的伪代码:

socket(套接字)就是一个javascript对象,对象的本质就是封装了属性与方法。socket中就封装了用于与对方通信时所需要的资源。

// 客户端 导入socket.io后
let socket = io('ws://localhost:3000/');

// 服务端 安装socket.io后
let socketio = require('socket.io');
socketio.on('connection', (socket)=>{
   
    连接建立成功后执行
});

建立websocket连接

服务端代码:
  1. 新建项目(socketserver文件夹)

安装express。安装socket.io

cd socketserver
npm init   // 一路回车  生成package.json
npm install --save express   // 安装express
npm install --save socket.io   // 安装socket.io
  1. 基于socket.io提供的API,接收客户端的连接请求,建立websocket连接。
// index.js   服务端核心js文件
const express = require('express');
const server = express();
// 调用server.get()  server.post() 注册路由 接收请求

// 配置websocket
// 获取express底层所使用的http模块
const http = require('http').createServer(server);
// websocket需要把通信相关路由注册到该底层http模块
const socketio = require('socket.io')(http);
socketio.on('connection', (socket)=>{
    
  console.log('有客户端进来了:'+socket.id)
});

// 设置静态资源托管目录 public
server.use(express.static('public'));

// 启动服务 不能再使用server.listen  而应该调用http.listen
// 因为socketio将通信相关路由注册到了http模块,而非express
// 所以http才可以接收到websocket请求,express则只用于处理http服务
http.listen(3000, function(){
    
  console.log('server is running...')
});
客户端代码:

为了避免跨域问题,设置静态资源托管目录public,把网页写在public中即可。

<script src="socket.io.js"></script>
<script>
btnConn.addEventListener('click', ()=>{
     
    let socket = io('ws://localhost:3000/');
    console.log(socket);
});
</script>

客户端与服务端之间进行通信

客户端发消息给服务端

客户端:

let socket = io('ws://localhost:3000/');
// 发消息    (emit 翻译为触发)
socket.emit('textmsg', '你瞅啥?!');  

服务端:

socketio.on('connection', (socket)=>{
   
    // 监听客户端发过来的消息
    socket.on('textmsg', (data)=>{
   
        console.log(data)
    });
});
服务端发消息给客户端

服务端代码:

socket.on('textmsg', (data)=>{
   
    console.log(data)
    // 回复
    socket.emit("textmsg", '瞅你咋地~~');
});

客户端代码:

let socket = io('ws://localhost:3000/');
socket.on('textmsg', (data)=>{
   
    alert('xxxx');
});
服务端向所有客户端广播消息
socketio.on('connection', (socket)=>{
   
    socket.emit();   // 针对当前客户端发消息
    socketio.emit('textmsg', '兄弟们!');   // 向所有客户端都发消息
});

例子:文件夹目录为
在这里插入图片描述

客户端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h2>websockt示例</h2>
    <button id="btnConn">建立连接</button>
    <button id="btnSend">向服务器端发消息</button>
    <button id="btnSend3">向服务器端发消息:请服务器端准备群发</button>
    <script src="socket.io.js"></script>
    <script>
        let socket;
        // 建立连接
        btnConn.addEventListener("click", ()=>{
   
            // socket = io('ws//localhost:3000/');
        socket = io('ws://localhost:3000/');
            console.log(socket);
            // 因为接收信息是在建立完连接之后就监听,所以接收服务器的信息是在连接之后就开启接收
            socket.on("textmsg", (data)=>{
   
                window.alert(data);
            });
        });
        // 客户端主动发消息
        btnSend.addEventListener("click", ()=>{
   
            // textmsg 是代表在服务器端接收时要用该textmsg去接收,只跟此次发送和接收相关,
            // 即每次接收和发送的要匹配即可
            socket.emit("textmsg", "向服务器发送信息,单发");
        });
        btnSend3.addEventListener("click", ()=>{
   
            // textmsg 是代表在服务器端接收时要用该textmsg去接收,只跟此次发送和接收相关,
            // 即每次接收和发送的要匹配即可
            socket.emit("textmsg", "向服务器端发消息:请服务器端准备群发");
        });
    </script>
    
</body>
</html>

服务端:

//index.js 服务端核心js文件
const express = require('express');
const server = express();

// 调用server.get() serve.post()注册路由,接收请求
// const socketio = require("socket.io");
// 建立服务端和socketio连接
// 获取express底层所使用的http模块,因为要通过express底层的http去发送请求,建立连接是用http,所以要获得http
const http = require('http').createServer(server);
// 要将express底层的http和socketio建立联系
const socketio = require('socket.io')(http);
// 监听连接事件,连接就触发回调函数
socketio.on("connection", (socket)=>{
   
    console.log(socket.id);

    // 1.服务器端主动发送消息
    // 1.1针对当前客户端发送消息
    // socket.emit("textmsg", "连接建立成功");
    // 1.2向所有客户端发送消息,通过socktio发送

    // 2.服务器端接收消息并发送消息
    // 通过socketio连接的是长连接,所以接收信息要在长链接接听中接收和处理
    socket.on('textmsg', (data)=>{
   //接收textmsg类型的消息,即可以理解为监听textmsg的信息,有该信息则进行回调函数调用
        console.log(data);
        // 回复客户端
        if(/向服务器发送信息,单发/.test(data)){
   
            socket.emit("textmsg", "回复客户端信息");
        }else{
   
            socketio.emit("textmsg", "给客户端们都群发信息");
        }
    });
});
// 如果之后要写接口,那么是用serve.get来接收,因为serve上绑定了socketio,所以可以直接用
// 页面有自动重连的功能,如果没有连接成功

// 设置静态资源托管目录 public,即可以通过服务器地址直接访问该文件夹内的资源
server.use(express.static('public'));

// 启动服务 不能再使用server.listen,而应该调用http.listen
// 因为sokectio将通信相关的路由注册到了http模块,而非express
// 所以http才可以接收到websockt请求,express则用于处理http服务
http.listen(3000, function(){
   
    console.log("serve is running");
})

实现群聊天室

需求如下:

  1. 在聊天界面中建立websocket连接。

  2. 当客户端点击发送按钮时,使用已经建立好的连接向服务端发送消息。服务端接收该消息后,将这个消息内容向所有已经连接的客户端再发一遍即可。

  3. 每个客户端需要监听服务端发回来的消息,控制台打印。

  4. 消息上屏。把接收到的消息显示在聊天记录区。

  5. 优化业务细节。

  6. 实时更新聊天室的人数。

    1. 在服务端实时维护一个表达在线人数的变量。count
    2. 一旦有客户端连接,count++
    3. 一旦有客户端断开连接,count--
    4. 无论count是递增了还是递减了,只要count有变化,就需要给所有客户端发消息emit('count_updated', count)
    5. 客户端监听并接收该消息类型,更新页面中的在线人数即可。
  7. 实现登录业务。

    1. index.html点击登录时,需要获取昵称(文本框里输入)与头像(随机生成文件名)。带着这两个参数一起跳转到chart.html

      // window.location.href = "chart.html"
      location = "chart.html?name=xxx&avatar=xx.jpg"
      
    2. chart.html中获取nameavatar,更新用户信息。

    3. 发消息时,不能只发送内容,而是需要发送一个对象:

      {content:'xxxx', name:'xx', avatar:'xxx.jpg'}

    4. 服务端接收后将原封不动的把对象发给所有客户端。一旦客户端接收到返回回来的对象,需要解析对象的属性,上屏。

聊天室文件夹结构为:

在这里插入图片描述

群聊完成了,私聊该如何解决?

每当客户端与服务端建立了一个websocket连接,客户端将会得到一个socket对象,服务端也会得到一个socket对象。这些socket对象用于与对方进行数据通信。所以服务端保存了与每个客户端通信所需要的的socket对象。

所以如何实现私聊?

亮亮发消息时,不仅需要发送消息内容,还需要发送对方的ID(也可以是name,总之得传递对方的唯一标识符)。服务端接收到该消息后,需要通过ID(也可以是name)找到与对方建立起来的socket连接对象,直接调用socket.emit()方法发消息即可。
但是这里依然存在着不足,这里在用户退出之后,没有清除对应的用户socket,所以这里依然要去不断完善。

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-sca
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值