SocketIo的使用和基于SocketIO的聊天室

news2024/11/16 2:23:10

  Socket.IO 是一个库,可以在客户端和服务器之间实现 低延迟, 双向 和 基于事件的 通信。

一、Socket.IO的特点

以下是 Socket.IO 在普通 WebSockets 上提供的功能:

1、HTTP 长轮询回退

如果无法建立 WebSocket 连接,连接将回退到 HTTP 长轮询。
这个特性是人们在十多年前创建项目时使用 Socket.IO 的原因(!),因为浏览器对 WebSockets 的支持仍处于起步阶段。
即使现在大多数浏览器都支持 WebSockets(超过97%),它仍然是一个很棒的功能,因为我们仍然会收到来自用户的报告,这些用户无法建立 WebSocket 连接,因为他们使用了一些错误配置的代理。

2、自动重新连接

在某些特定情况下,服务器和客户端之间的 WebSocket 连接可能会中断,而双方都不知道链接的断开状态。
这就是为什么 Socket.IO 包含一个心跳机制,它会定期检查连接的状态。
当客户端最终断开连接时,它会以指数回退延迟自动重新连接,以免使服务器不堪重负。

3、数据包缓冲

当客户端断开连接时,数据包会自动缓冲,并在重新连接时发送。

4、收到后的回调

Socket.IO 提供了一种方便的方式来发送事件和接收响应

5、广播

在服务器端,您可以向所有连接的客户端或客户端的子集发送事件

6、多路复用

命名空间允许您在单个共享连接上拆分应用程序的逻辑。例如,如果您想创建一个只有授权用户才能加入的“管理员”频道

更多信息请访问socketIO的介绍文档

二、Socket.IO发送消息常见的方式

  socket.io用on函数给调用的时间注册调用函数,用emit函数来发送时间,以此来时间客户端和服务器两端的通信,常用的使用方式如下:

1、只发送事件

发送端代码:

socket.emit('action');

表示发送了一个action命令,命令是字符串的,在另一端接收时,可以这么写:

socket.on('action',function(){
	...
});

2、发送事件和一个数据

发送端:

socket.emit('action',data);

表示发送了一个action命令,还有data数据,在另一端接收时,可以这么写:

socket.on('action',function(data){
	...
});

3、发送事件和多个数据

发送端:

socket.emit('action',arg1,arg2);

表示发送了一个action命令,还有两个数据,在另一端接收时,可以这么写:

socket.on('action',function(arg1,arg2){
	...
});

如果是多个参数,就在后面加参数就行了

4、发送事件和数据及回调函数

在emit方法中包含回调函数,例如:

socket.emit('action',data, function(arg1,arg2){
	...
} );

那么这里面有一个回调函数可以在另一端调用,另一端可以这么写:

socket.on('action',function(data,fn){ 
    ...
    fn('a','b');
    ...
 });

三、emit发送消息的范围

下面是emit不同方法发送消息的范围:

  // 只发给sender。 sending to the client
  socket.emit('hello', 'can you hear me?', 1, 2, 'abc');

  // 发给所有人,除了sender。 sending to all clients except sender
  socket.broadcast.emit('broadcast', 'hello friends!');

  // 发给game房间所有人,除了sender。 sending to all clients in 'game' room except sender
  socket.to('game').emit('nice game', "let's play a game");

  // 发给game1和/或game2所有人,除了sender。 sending to all clients in 'game1' and/or in 'game2' room, except sender
  socket.to('game1').to('game2').emit('nice game', "let's play a game (too)");

  // 发给game房间所有人,包含sender。 sending to all clients in 'game' room, including sender
  io.in('game').emit('big-announcement', 'the game will start soon');

  // 发给域名myNamespacs所有人,包含sender。 sending to all clients in namespace 'myNamespace', including sender
  io.of('myNamespace').emit('bigger-announcement', 'the tournament will start soon');

  // 发给域名myNamespace里room房间的所有人,包含sender。 sending to a specific room in a specific namespace, including sender
  io.of('myNamespace').to('room').emit('event', 'message');

  // 发给某一个人 sending to individual socketid (private message)
  io.to(`${socketId}`).emit('hey', 'I just met you');

四、聊天室实现

1、项目结构

在这里插入图片描述

2、server.js

server.js实现了一个web服务器,主要的功能有两个

  • 实现一个nodejs的服务器,让浏览器能够加载到聊天网页
  • 用socket.io实现服务端接受客户连接和发送消息的功能
    其代码如下:
'use strict'

// 配置日志
const log4j = require('log4js');
const logger = log4j.getLogger();

// 配置http服务器
const {createServer} = require('http');
const express = require('express');
const serveIndex = require('serve-index');

const app = express();
// 配置静态文件,位置不能乱
app.use(serveIndex('./public'));
app.use(express.static('./public'));

// 创建http服务器
const http_server = createServer(app);

// 配置socket io
const {Server} = require("socket.io");
// 让socketIo监听https服务器

const io = new Server(http_server);


// 配置socket发送消息逻辑
io.on('connection', (socket) => {
    logger.info('socket connection : ', socket);
    //socket.emit('joined',room,socket.id);只给当前用户发送
    //socket.to(room).emit('joined', room, socket.id);//除自己之外
    //io.in(room).emit('joined', room, socket.id)//房间内所有人
    //socket.broadcast.emit('joined', room, socket.id);//除自己,全部站点
    // 加入房间的处理逻辑
    socket.on('join', (room, userName) => {
        logger.info(`user join in. userName=${userName},room=${room}`);
        // socket加入room中
        socket.join(room);
        // 发送给当前用户用户加入成功
        socket.emit('joined', room, socket.id);
    });

    // 离开房间的处理逻辑
    socket.on('leave', (room, userName) => {
        logger.info(`user leave. userName=${userName},room=${room}`);
        socket.leave(room);
        socket.emit('leaved', room, socket.id);
    })

    // 发送消息逻辑
    socket.on('message', (room, data) => {
        // 给房间内的所有人发送消息(包括自己)
        io.in(room).emit('message', data);
        //不给自己发,只给别人发(前端需要适配自己发送的内容到消息显示框)
        //socket.to(room).emit('message', data);
    })

});

//启动服务器
http_server.listen(80);

3、index.html

index.html是聊天的网页,代码如下:

<html>
<head>
    <title>Chat room</title>
    <link rel="stylesheet" href="./css/main.css">
</head>

<body>
<table align="center">
    <tr>
        <td>
            <label>UserName:</label>
            <input type="text" id="userName">
        </td>
    </tr>
    <tr>
        <td>
            <label>Room:</label>
            <input type="text" id="room">
            <button id="connect">Connect</button>
            <button id="leave">Leave</button>
        </td>
    </tr>
    <tr>
        <td>
            <label>Content: </label><br>
            <textarea disabled style="line-height: 1.5;" id="content" rows="10" cols="100"></textarea>
        </td>
    </tr>
    <tr>
        <td>
            <label>Input: </label><br>
            <textarea disabled id="input" rows="3" cols="100"></textarea>
        </td>
    </tr>
    <tr>
        <td>
            <button disabled id="send">Send</button>
        </td>
    </tr>
</table>


<script src="/socket.io/socket.io.js"></script>
<script src="./js/client.js"></script>
</body>

</html>

4、main.css

main.css是index.html的布局格式文件,代码如下:

/*
 *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree.
 */
button {
  margin: 0 20px 25px 0;
  vertical-align: top;
  width: 134px;
}

div#getUserMedia {
  padding: 0 0 8px 0;
}

div.input {
  display: inline-block;
  margin: 0 4px 0 0;
  vertical-align: top;
  width: 310px;
}

div.input > div {
  margin: 0 0 20px 0;
  vertical-align: top;
}

div.output {
  background-color: #eee;
  display: inline-block;
  font-family: 'Inconsolata', 'Courier New', monospace;
  font-size: 0.9em;
  padding: 10px 10px 10px 25px;
  position: relative;
  top: 10px;
  white-space: pre;
  width: 270px;
}

section#statistics div {
  display: inline-block;
  font-family: 'Inconsolata', 'Courier New', monospace;
  vertical-align: top;
  width: 308px;
}

section#statistics div#senderStats {
  margin: 0 20px 0 0;
}

section#constraints > div {
  margin: 0 0 20px 0;
}

section#video > div {
  display: inline-block;
  margin: 0 20px 0 0;
  vertical-align: top;
  width: calc(50% - 22px);
}

section#video > div div {
  font-size: 0.9em;
  margin: 0 0 0.5em 0;
  width: 320px;
}

h2 {
  margin: 0 0 1em 0;
}

section#constraints label {
  display: inline-block;
  width: 156px;
}

section {
  margin: 0 0 20px 0;
  padding: 0 0 15px 0;
}

section#video {
  width: calc(100% + 20px);
}

video {
  --width: 90%;
  display: inline-block;
  width: var(--width);
  height: calc(var(--width) * 0.75);
  margin: 0 0 10px 0;
}

@media screen and (max-width: 720px) {
  button {
    font-weight: 500;
    height: 56px;
    line-height: 1.3em;
    width: 90px;
  }

  div#getUserMedia {
    padding: 0 0 40px 0;
  }

  section#statistics div {
    width: calc(50% - 14px);
  }

  video {
    display: inline-block;
    width: var(--width);
    height: 96px;
  }
}

5、client.js

client.js文件是客户端的处理逻辑文件,包括发送和显示消息,连接服务器等

'use strict'

// 获取页面组建
const userNameInput = document.querySelector('input#userName');
const roomInput = document.querySelector('input#room');

const connectBtn = document.querySelector('button#connect');
const leaveBtn = document.querySelector('button#leave');

const contentArea = document.querySelector('textarea#content');
const inputArea = document.querySelector('textarea#input');

const sendBtn = document.querySelector('button#send');

var socket;
// 连接逻辑
connectBtn.onclick = () => {
    //连接
    socket = io();

    // 成功加入后的逻辑
    socket.on('joined', (room, id) => {
        console.log(`join in successful,room=${room},socketId=${id}`);
        connectBtn.disabled = true;
        leaveBtn.disabled = false;
        inputArea.disabled = false;
        sendBtn.disabled = false;
        roomInput.disabled = true;
        userNameInput.disabled = true;
    });

    //离开成功的逻辑
    socket.on('leaved', (room, id) => {
        console.log(`user leave ,room=${room},socketId=${id}`);
        connectBtn.disabled = false;
        leaveBtn.disabled = true;
        inputArea.disabled = true;
        sendBtn.disabled = true;
        roomInput.disabled = false;
        userNameInput.disabled = false;
        socket.disconnect();
    });

    // 断开连接
    socket.on('disconnect', (socket) => {
        connectBtn.disabled = false;
        leaveBtn.disabled = true;
        inputArea.disabled = true;
        sendBtn.disabled = true;
        roomInput.disabled = false;
        userNameInput.disabled = false;
    });

    // 接受到消息的逻辑
    socket.on('message', (data) => {
        //窗口总是显示最后的内容
        contentArea.scrollTop = contentArea.scrollHeight;
        contentArea.value = contentArea.value + data + '\r';
    });

    // 发送加入的信令
    socket.emit('join', roomInput.value, userNameInput.value);
}

//断开连接
leaveBtn.onclick = () => {
    socket.emit('leave', roomInput.value, userNameInput.value);
}

//发送消息的逻辑
sendBtn.onclick = () => {
    sendMessage();
}

// 回车发送消息的逻辑
inputArea.onkeypress = (event) => {
    //回车发送消息
    if (event.keyCode !== 13) {
        return;
    }
    sendMessage();
    //阻止默认行为
    event.preventDefault();
}

function sendMessage() {
    let data = userNameInput.value + ' : ' + inputArea.value;
    socket.emit('message', roomInput.value, data);
    inputArea.value = '';
}

6、启动服务器

找到项目所在的目录,安装项目需要的模块

npm install express serve-index log4js socket.io

用以下命令启动服务器

node server.js

7、功能演示

打开两个浏览器的窗口,分别输入项目地址http://localhost/index.html(如果是别的ip,将localhost换成对应的ip地址),在浏览器上面输入用户名(不同),房间room(相同),点击connect按钮就可以发送消息。
在这里插入图片描述
另一个客户端:
在这里插入图片描述


后记
  个人总结,欢迎转载、评论、批评指正

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1234561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

随笔-事儿就这么个事儿

好久没写了&#xff0c;小A要催更&#xff0c;还答应让我写一下他的经历&#xff0c;这还有啥说的&#xff0c;开整。 1、升级 前段时间登录公司的办公系统处理一个事务申请&#xff0c;发现有个粗体标红的通知&#xff0c;是关于今年的晋升名单公示。进去看了一眼&#xff0…

姓氏情侣家庭亲子谐音顽梗头像分销流量主微信抖音小程序开发

姓氏情侣家庭亲子谐音顽梗头像分销流量主微信抖音小程序开发 姓氏情侣头像&#xff1a;提供各种姓氏的情侣头像模板&#xff0c;用户可根据自己的姓氏选择合适的头像进行定制。 家庭头像&#xff1a;为家庭成员提供多种形式的头像模板&#xff0c;让用户可以选择合适的家庭头像…

MAX/MSP SDK学习05:A_GIMME方法

今天终于将A_GIMME方法部分的描述看懂了&#xff0c;上周因为太赶时间加上这文档很抽象一直没看懂。也就那么一回事&#xff0c;记录一下。 A_GIMME方法用于接收多个参数&#xff1a; #include "ext.h" // standard Max include, always required #include "…

黑马React18: 基础Part II

黑马React: 基础2 Date: November 16, 2023 Sum: 受控表单绑定、获取DOM、组件通信、useEffect、Hook、优化B站评论 受控表单绑定 受控表单绑定 概念&#xff1a;使用React组件的状态&#xff08;useState&#xff09;控制表单的状态 准备一个React状态值 const [value, se…

安全领航,共筑敏捷开发新时代【云驻共创】

安全领航&#xff0c;共筑敏捷开发新时代。网络安全形势虽然严峻&#xff0c;但得益于企业安全意识的提升&#xff0c;近两年来遭受网络攻击的网站不断减少&#xff0c;普通网民的个人隐私及其他敏感数据得到了更多的保证。华为云基于自身多年的安全经验研发了可以帮助开发者实…

前端vite打包工具

文章目录 vite简介特点搭建步骤 vite简介特点 Vite是在推出Vue 3的时候开发的&#xff0c;目前仅支持Vue 3.x&#xff0c;这意味着与Vue 3不兼容的库也不能与Vite一起使用。持热更新&#xff0c;而且热更新的速度不会随着模块增多而变慢。在生产环境下使用Rollup打包。Vite 的…

湖科大计网:应用层

一、应用层概述 交互&#xff0c;实现特定问题&#xff01; 二、客户与服务器模型 一、C/S 客户/服务器方式 服务与被服务的关系。 二、P2P方式 对等方式 P2P方式是对等的&#xff0c;没有固定的服务器。 三、DNS域名系统 DNS&#xff08;Domain Name System&#xff09; 一、域…

CF 1894A 学习笔记 思维 题意理解分析

原题 A. Secret Sport time limit per test 3 seconds memory limit per test 512 megabytes input standard input output standard output Lets consider a game in which two players, A and B, participate. This game is characterized by two positive integer…

NSSCTF web刷题记录6

文章目录 [HZNUCTF 2023 final]eznode[MoeCTF 2021]地狱通讯-改[红明谷CTF 2022] Smarty Calculator方法一 CVE-2021-26120方法二 CVE-2021-29454方法三 写马蚁剑连接 [HZNUCTF 2023 final]eznode 考点&#xff1a;vm2沙箱逃逸、原型链污染 打开题目&#xff0c;提示找找源码 …

前端环境变量释义

视频教程 彻底搞懂前端环境变量使用和原理&#xff0c;超清楚_哔哩哔哩_bilibili 添加命令行参数 --modexxxxx 新建.env.xxxx文件,其中.env文件会在所有环境下生效 以VITE_开头&#xff0c;字符串无需加双引号 使用import.meta.env.VITE_xxxxx进行调用

全网最全jmeter接口测试/接口自动化测试看这篇文章就够了:跨线程组传递jmeter变量及cookie的处理

setUp线程组 setUp thread group&#xff1a; 一种特殊类型的线程组&#xff0c;用于在执行常规线程组之前执行一些必要的操作。 在 setup线程组下提到的线程行为与普通线程组完全相同。不同的是执行顺序--- 它会在普通线程组执行之前被触发&#xff1b; 应用场景举例&#xf…

UE4基础篇十六:自定义 EQS 生成器

UE4 中的 EQS 带有一组很好的查询项生成器,但在某些情况下,您可能更喜欢根据需要创建生成器。我决定编写自己的生成器,因为我必须编写一个查询来找到查询器周围的最佳位置,但又不能太靠近它。我知道我可以添加一个距离测试来随着距离增加分数,但我什至不想考虑距查询器一定…

FreeRTOS源码阅读笔记4--semphr.h

信号量是特殊的队列--无法存储消息的队列&#xff0c;相关的接口函数声明在semphr.h中&#xff0c;通过宏定义替换队列函数实现。 4.1创建二值信号量xSemaphoreCreateBinary() 4.1.1函数原型 queueQUEUE_TYPE_BINARY_SEMAPHORE&#xff1a;一个宏&#xff0c;表示创建队列的…

2024湖南师范大学计算机考研分析

24计算机考研|上岸指南 湖南师范大学 湖南师范大学创建于1938年&#xff0c;位于历史文化名城长沙&#xff0c;是国家“211工程”重点建设大学、国家“双一流”建设高校、教育部普通高等学校本科教学工作水平评估优秀高校。学校拥有6个“十二五”国家重点学科、21个博士学位授…

【Python爬虫】8大模块md文档集合从0到scrapy高手,第7篇:selenium 数据提取详解

本文主要学习一下关于爬虫的相关前置知识和一些理论性的知识&#xff0c;通过本文我们能够知道什么是爬虫&#xff0c;都有那些分类&#xff0c;爬虫能干什么等&#xff0c;同时还会站在爬虫的角度复习一下http协议。 爬虫全套笔记地址&#xff1a; 请移步这里 共 8 章&#x…

股票池(三)

3-股票池 文章目录 3-股票池一. 查询股票池支持的类型二. 查询目前股票池对应的股票信息三 查询股票池内距离今天类型最少/最多的股票数据四. 查询股票的池统计信息 一. 查询股票池支持的类型 接口描述: 接口地址:/StockApi/stockPool/listPoolType 请求方式&#xff1a;GET…

OpenLayers实战,WebGL图层根据Feature要素的变量动态渲染多种颜色的三角形,适用于大量三角形渲染不同颜色

专栏目录: OpenLayers实战进阶专栏目录 前言 本章使用OpenLayers根据Feature要素的变量动态渲染不同颜色的三角形。 通过一个WebGL图层生成四种不同颜色的图形要素,适用于WebGL图层需要根据大量点要素区分颜色显示的需求。 更多的WebGL图层使用运算符动态生成样式的内容将会…

如何搭建测试环境?一文解决你所有疑惑!

什么是测试环境 测试环境&#xff0c;指为了完成软件测试工作所必需的计算机硬件、软件、网络设备、历史数据的总称&#xff0c;简而言之&#xff0c;测试环境硬件软件网络数据准备测试工具。 硬件&#xff1a;指测试必需的服务器、客户端、网络连接等辅助设备。 软件&#…

5年经验之谈 —— 性能测试如何定位分析性能瓶颈?

你好&#xff0c;我是小牛&#xff0c;目前在一家准一线互联网大厂做测试开发工程师。 对于一般公司普通测试工程师来说&#xff0c;可能性能测试做的并不是很复杂&#xff0c;可能只是编写下脚本&#xff0c;做个压测&#xff0c;然后输出报告结果&#xff0c;瓶颈分析和调优…

SQL基础理论篇(八):视图

文章目录 简介创建视图修改视图删除视图总结参考文献 简介 视图&#xff0c;即VIEW&#xff0c;是SQL中的一个重要概念&#xff0c;它其实是一种虚拟表(非实体数据表&#xff0c;本身不存储数据)。 视图类似于编程中的函数&#xff0c;也可以理解成是一个访问数据的接口。 从…