WebSocket详解以及应用
- 😜作 者:是江迪呀
- ✒️本文关键词:
websocket
、网络
、长连接
、前端
- ☀️每日 一言:
任何一个你不喜欢而又离不开的地方,任何一种你不喜欢而又无法摆脱的生活,都是监狱!
一、前言
我们在日常开发中是否会思考,为什么一个系统在没有任何请求的前提下,会接受到服务器端发来的消息?聊天软件是为什么可以做到消息的发送和实时接收?网络游戏中为什么我可以在我们屏幕中看到其它玩家的操作?今天这篇关于WebSocket
的文章完全可以解决你的疑问。废话不多说,让我们开始吧!
二、Websocket
介绍
2.3 什么是Websocket
?
WebSocket
是一种全双工通信协议,它能够在单个TCP
连接上实现 双向、持久的实时通信, 无需频繁地发起连接和关闭连接。通过WebSocket
,我们可以在浏览器和服务器之间建立稳定的连接,实时传递数据,实现即时聊天、实时更新、多人在线游戏等功能。
2.2 Websocket
的特点
(1)双向通信: WebSocket
允许客户端和服务器之间进行双向通信,无需等待对方的请求或响应。
(2) 持久连接: WebSocket
连接一旦建立,会持续保持连接状态,避免了重复的连接和断开过程,减少了网络开销。
(3)低延迟: 由于连接一直保持打开状态,数据的传输可以更快地实现,从而实现低延迟的实时通信。
(4) 较少的数据传输量: 与传统的HTTP
请求相比,WebSocket
传输的数据头部信息较少,减少了数据传输量,提高了效率。
(5) 协议支持: WebSocket
是一种独立于应用层协议的协议,可以在多种编程语言和平台上使用。
2.3 Websocket
和Http
区别
(1)通信方式区别:
- Http是请求-响应式的协议: 客户端发送请求,服务器返回响应,然后连接断开。这种方式适合传输静态内容或需要客户端不断向服务器发起请求的情况。
- Websocket是全双工的通信协议:
WebSocket
允许在客户端和服务器之间建立持久性的连接,双方可以随时相互发送数据。这种方式适合实时性要求较高、交互复杂的应用,如实时聊天、在线游戏等。
(2)连接状态区别:
- Http是短连接:web端请求服务器端建立连接,服务器端返回后连接断开。
- Websocket是长连接:连接一次,就会一直保持连接。
Http
相当于写信,需要一来一回。
Websocket
相当于打电话,只要不挂断,咱们可以一直通话。
三、实现方式
我采用 客户端:Client
和 服务器端:Service
的方式来展示WebSocket
的实现方式。客户端使用JS
实现服务器端使用Java
+ SpringBoot
来实现。
3.1 客户端
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Websocket</title></head><body><input id="messageInput" placeholder="消息"><button id="sendButton">发送</button></body>
</html>
<script>// url 路径最后的 1 表示是那个客户端const websocket = new WebSocket('ws://192.168.31.136:5050/websocket/1');websocket.onmessage = event => {const data = JSON.parse(event.data);// 处理服务器发送过来的消息console.log('Received message:', data);};sendButton.addEventListener('click', () => {const message = messageInput.value;const messageData = {message: message};//发送消息到服务端websocket.send(JSON.stringify(messageData));messageInput.value = ''; // 清空输入框});window.addEventListener('beforeunload', () => {// 在页面关闭前关闭 WebSocket 连接websocket.close();});
</script>
3.2 服务器端
引入依赖:
<!--SpringBoot依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.1.2.RELEASE</version></dependency><!--Websocket依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId><version>2.3.7.RELEASE</version>
</dependency>
SpringBoot
启动类:
@SpringBootApplication
@EnableWebSocket
public class ApplicationRun {public static void main(String[] args) {SpringApplication.run(ApplicationRun.class,args);}
}
这里注意一定要添加上@EnableWebSocket
,表示开启Websocket
。
application.ymal
配置文件:
server:port: 5050
Websocket
服务端:
package com.hjd.websocket.websocketserver;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;@Component
@ServerEndpoint("/websocket/{clientId}")
@Slf4j
public class MsgWebSocketServer {/*** 连接成功后会调用的方法*/@OnOpenpublic void webSocketOpen(@PathParam("clientId") String clientId) {log.info("客户端{}连接上了服务端:", clientId);}/*** 关闭连接会调用方法* 关闭方法在服务器端停止或者是客户端页面关闭时都会被调用。* 或者是客户端调用了关闭链接的方法。*/@OnClosepublic void onClose() {log.info("有客户端断开了连接!");}/*** 接收到消息会调用的方法*/@OnMessagepublic void onMessage(@PathParam("clientId") String clientId,String message) {log.info("客户端:{}发来了消息:{}",clientId,message);}/*** 出现错误时调用的方法*/@OnErrorpublic void onError(Session session, Throwable error) {log.info("服务器端发生了错误!");}}
3.3 测试 - 客户端发消息给服务器端
我们运行客户端在浏览器。
我们可以看到服务器端打印了如下日志:
表示已经连接上了。
在客户端发送消息给服务器端:
服务器端接收到了消息:
3.4 测试- 服务器端推送信息给客户端
要想服务器端推送消息给客户端,有个问题是服务器端如何知道你要发送给那个客户端呢?所以就必须存储下已经连接到服务器端的客户端消息。需要改动下代码:
Websocket
服务端(省略了onError
和onClose
方法它们不变):
@Component
@ServerEndpoint("/websocket/{clientId}")
@Slf4j
public class MsgWebSocketServer {/*** 用来存放已经成功连接到服务器端的客户端*/private static ConcurrentHashMap<String, MsgWebSocketServer> webSocketSet = new ConcurrentHashMap<>();private Session session;/*** 客户端标识*/private String clientId;/*** 连接成功后调用的方法*/@OnOpenpublic void webSocketOpen(Session session,@PathParam("clientId") String clientId) {this.saveClient(session,clientId);log.info("客户端:{}连接上了服务端", clientId);}@OnMessagepublic void onMessage(@PathParam("clientId") String clientId,String message,@PathParam("toClientId") String toClientId) throws Exception {log.info("客户端:{}发来了消息:{}",clientId,message);//给指定的客户端推送消息if(toClientId != null){webSocketSet.get(toClientId).sendMessage(message);}}/*** 保存客户端* @param session* @param clientId*/private void saveClient(Session session,String clientId){this.session = session;this.clientId = clientId;if (webSocketSet.containsKey(clientId)) {webSocketSet.remove(clientId);webSocketSet.put(clientId, this);} else {webSocketSet.put(clientId, this);}}/*** 发送消息* @param message* @throws Exception*/private void sendMessage(String message) throws Exception {this.session.getBasicRemote().sendText(message);}
}
使用调接口的方式,触发服务器推送消息动作:
package com.hjd.websocket.controller;import com.hjd.websocket.websocketserver.MsgWebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import javax.websocket.server.PathParam;@RequestMapping("/send")
@RestController
public class WebSocketController {@Autowiredprivate MsgWebSocketServer msgWebSocketServer;/*** 发送消息给指定客户端* @param msg* @param toClient* @throws Exception*/@GetMapping("/sendMsgToClient/{msg}")public void getIndex(@PathParam("msg") String msg,@PathParam("toClient") String toClient) throws Exception {msgWebSocketServer.onMessage(null,msg,toClient);}}
调用推送消息接口:
http://localhost:5050/send/sendMsgToClient?msg=%22%E5%93%88%E5%93%88%E5%93%88%E5%93%88%22&toClient=1
客户端控制台:
3.5 测试 - 广播给所有客户端
这个也非常简单,我们已经把连接上的客户端全部存储到了ConcurrentHashMap
集合中(之所以使用ConcurrentHashMap
是因为它线程安全,可以保证再多个客户端链接时,存储的信息不会错乱)只需要遍历下集合,给每个客户端都发送消息即可实现广播的效果了。
四、应用
4.1 聊天室
基于上面的讲解,我们在客户端发送消息时指定下接受消息的toClient
就搞定了。
4.2 多人网络游戏
这个思路也比较简单,不过得分房间,比如一个房间内有四个玩家,其中一个玩家操作了游戏中的对象,只需要将对应的操作指令发送给服务器端,然后广播给房间内的所有玩家,那么就可以实现游戏世界的状态同步,让玩家能够看到其他玩家的操作了。
4.3 服务器推送
3.5测试演示的就是。
总结
最后通过一张图来说明吧!
我想使用Websocket
实现一个多人在线游戏作为示例更加充分的说明Websocket
的应用。等我吃60个汉堡
再说吧~!
相关文章:
WebSocket详解以及应用
😜作 者:是江迪呀✒️本文关键词:websocket、网络、长连接、前端☀️每日 一言:任何一个你不喜欢而又离不开的地方,任何一种你不喜欢而又无法摆脱的生活,都是监狱! 一、前言 我们在…...
如何评估开源项目的活跃度和可持续性?
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...
远程Linux/ubuntu服务器后台不间断运行py文件/sh脚本
通常我们在生产环境中运行一些项目时需要将程序不间断的运行在服务器上,并且将日志文件打印到某个文件中,直到程序运行结束,下面介绍了在Linux服务器上不间断运行py文件的方式,以及如何保存相应的日志信息。 对于 .py 文件&#x…...
记录一个诡异的bug
将对接oa跳转到会议转写的项目oa/meetingtranslate项目发布到天宫,结果跳转到successPage后报错 这一看就是successPage接口名没对上啊,查了一下代码,没问题啊。 小心起见,我就把successPage的方法请求方式从Post改为Get和POST都…...
Xamarin.Android中的Fragment
目录 1、Activity中使用Fragment2、Fragment与Activity通信3、Fragment与其他的Fragment通信 1、Activity中使用Fragment 一般而言,会在activity中添加一个加载fragment的方法。通过点击菜单的按钮,加载不同的fragment。其样子一般是这样的:…...
portainer初体验
官方文档 安装 docker 这里采用的的是国内汉化的一个镜像,版本号2.16.2。 地址 docker run -d --restartalways --name"portainer" -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock 6053537/portainer-ce体验 访问9000端口。 尝试&#x…...
4G数传方案(合宙cat1模块)
一. 合宙Cat1简介 合宙 Air724 模组推出的低功耗,超小体积,高性能嵌入式 4G Cat1 核心版,标准的 2.54 排针、最小成本的进项 2G、4G Cat4 切换;主要功能如下: 实际测试工作环境为-35℃-75℃; 支持 5-12V 供电或者 3.7…...
ElasticSearch - 海量数据索引拆分的一些思考
文章目录 困难解决方案初始方案及存在的问题segment merge引入预排序 拆分方案设计考量点如何去除冗余数据按什么维度拆分,拆多少个最终的索引拆分模型演进历程整体迁移流程全量迁移流程流量回放比对验证异步转同步多索引联查优化效果 总结与思考参考 困难 索引数据…...
【SA8295P 源码分析】83 - SA8295P HQNX + Android 完整源代码下载方法介绍
【SA8295P 源码分析】83 - SA8295P HQNX + Android 完整源代码下载方法介绍 一、高通官网 Chipcode 下载步骤介绍1.1 高通Chipcode 下载步骤1.2 高通 ReleaseNote 下载方法二、高通 HQX 代码介绍2.1 完整的 HQX 代码结构:sa8295p-hqx-4-2-4-0_hlos_dev_qnx.tar.gz2.2 sa8295p-…...
【设计模式--原型模式(Prototype Pattern)
一、什么是原型模式 原型模式(Prototype Pattern)是一种创建型设计模式,它的主要目的是通过复制现有对象来创建新的对象,而无需显式地使用构造函数或工厂方法。这种模式允许我们创建一个可定制的原型对象,然后通过复制…...
初识 Redis
初识 Redis 1 认识NoSQL1.1 结构化与非结构化1.2 关联和非关联1.3 查询方式1.4. 事务1.5 总结 2 Redis 概述2.1 应用场景2.2 特性 3 Resis 全局命令4 Redis 基本数据类型4.1 String4.1.1 常用命令4.1.2 命令的时间复杂度4.1.3 使用场景 4.2 Hash4.2.1 常用命令4.2.2 命令的时间…...
php灵异事件,啥都没干数据变了?
这篇文章也可以在我的博客查看 搞WordPress,难免跟php打交道 然而这弱类型语言实在坑有点多 这不今儿又踩了个大坑直接时间-1😅 问题 话不多说直接上代码 <?php $items [1,2];foreach ($items as &$item) {/*empty loop*/} print_r($items)…...
【ffmpeg】基于需要使用videocapture的opencv编译配置(C++)
目录 配置简介ffmpeg源码编译方法记录gstreamer命令行安装方法opencv的编译项记录 配置简介 opencv使用videocapture读取视频流时,需要借助底层的ffmpeg库。如果不能正确编译,会报错,现记录正确编译配置方法。 ffmpeg源码编译方法记录 ope…...
Redisson分布式锁 原理源码 分析
# 基于setnx实现的分布式锁存在的问题: # 为了解决上面的问题,可以用Redisson # Redisson入门 # Redisson可重入锁原理 获取锁的Lua脚本: 释放锁的Lua脚本: # 锁重试原理分析 tryLock()底层代码分析 tim…...
Cocos独立游戏开发框架中的事件管理器
引言 本系列是《8年主程手把手打造Cocos独立游戏开发框架》,欢迎大家关注分享收藏订阅。在独立游戏开发中,事件管理器是一个不可或缺的组件。它为开发者提供了一种灵活的方式来处理游戏内部各种状态变化和用户交互,实现模块之间的解耦和通信…...
keepalived+haproxy 搭建高可用高负载高性能rabbitmq集群
一、环境准备 1. 我这里准备了三台centos7 虚拟机 主机名主机地址软件node-01192.168.157.133rabbitmq、erlang、haproxy、keepalivednode-02192.168.157.134rabbitmq、erlang、haproxy、keepalivednode-03192.168.157.135rabbitmq、erlang 2. 关闭三台机器的防火墙 # 关闭…...
网络安全(黑客)零基础自学
网络安全是什么? 网络安全,顾名思义,网络上的信息安全。 随着信息技术的飞速发展和网络边界的逐渐模糊,关键信息基础设施、重要数据和个人隐私都面临新的威胁和风险。 网络安全工程师要做的,就是保护网络上的信息安…...
如何把本地项目上传github
一、在gitHub上创建新项目 【1】点击添加()-->New repository 【2】填写新项目的配置项 Repository name:项目名称 Description :项目的描述 Choose a license:license 【3】点击确定,项目已在githu…...
跳跃游戏【贪心算法】
跳跃游戏 给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。在这里插入图片…...
vue2+element-ui 实现下拉框滚动加载
一、自定义滚动指令。 VUE.directive( el-select-loadmore: { bind(el, binding) { const SELECTWRAP_DOM el.querySelector(.el-select-dropdown .el-select-dropdown__wrap) SELECTWRAP_DOM.addEventListener(scroll, function () { /*…...
探索AIGC人工智能(Midjourney篇)(二)
文章目录 利用Midjourney进行LOGO设计 用ChatGPT和Midjourney的AI绘画,制作儿童绘本故事 探索Midjourney换脸艺术 添加InsightFaceSwap机器人 Midjourney打造专属动漫头像 ChatGPT Midjourney画一幅水墨画 Midjourney包装设计之美 Midjourney24节气海报插画…...
01-Flask-简介及环境准备
Flask-简介及环境准备 前言简介特点Flask 与 Django 的比较环境准备 前言 本篇来介绍下Python的web框架–Flask。 简介 Flask 是一个轻量级的 Web 框架,使用 Python 语言编写,较其他同类型框架更为灵活、轻便且容易上手,小型团队在短时间内…...
【Git游戏】远程分支
origin/<branch> 远程分支在本地以 origin/<branch>格式存在,他指向上次和远程分支通过时的记录 git checkout origin/<branch> 会出现HEAD分离的情况 与远程通讯 git fetch —— 从远端获取数据(实际上将本地仓库中的远程分支更新…...
Day07-ElementUI
Day02-ElementUI 一 菜单设计 1 静态菜单 a 在components文件夹中新建一个组件Menu.vue <template><div class="menu-wrap"><el-menuclass="el-menu-vertical-demo"background-color="#031627"text-color="#fff"ac…...
【Go 基础篇】Go语言中的defer和recover:优雅处理错误
Go语言以其简洁、高效和强大的特性受到了开发者的热烈欢迎。在错误处理方面,Go语言提供了一种优雅的机制,即通过defer和recover组合来处理恐慌(panic)错误。本文将详细介绍Go语言中的defer和recover机制,探讨其工作原理…...
4.15 TCP Keepalive 和 HTTP Keep-Alive 是一个东西吗?
目录 HTTP 的 Keep-Alive TCP 的 Keepalive 总结: HTTP的Keep-Alive,是应用层(用户态)实现的,称为HTTP长连接; TCP的Keepalive,是由TCP层(内核态)实现的,…...
如何在VSCode中将html文件打开到浏览器
天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…...
2022年03月 C/C++(四级)真题解析#中国电子学会#全国青少年软件编程等级考试
第1题:拦截导弹 某国为了防御敌国的导弹袭击, 发展出一种导弹拦截系统。 但是这种导弹拦截系统有一个缺陷: 虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。 某天, 雷达捕捉到敌国的…...
五公里场地训练笔记(完整版)
由于考研和口罩等原因,停跑了比较长的时间。中长距离就是这样,修为尽失,大概是要从头开始了,不过还是要乐观的面对,CHEER UP! 翻看咕咚软件,以前的PB是21:12,在2017年9月…...
【电能质量扰动】基于ML和DWT的电能质量扰动分类方法研究(Matlab实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
学生个人网站设计/厦门关键词优化企业
前言 19年初在上一家公司离职,在上一公司服役了4年半,成长了不少,收获了不少东西,在上一公司也带过很多团队,多的时候6-7个人,少的时候2人,也总结了很多的所谓的经验吧,由于一系列原…...
学做旗袍衣服的网站/游戏推广员一个月能赚多少
2022年中国国际服务贸易交易会(以下简称“服贸会”)落下帷幕,对于第二次参加服贸会的亚马逊云科技,面对不断加速的科技发展,如何更好地帮助企业抓住数字机遇,推动服务智能化转型是其持续深耕重要方向。亚马…...
用adsl做网站备案/专业恶意点击软件
B树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right); 2.所有结点存储一个关键字; 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树; 如: …...
建立网站需要多少钱 激发湖南岚鸿/优化营商环境评价
浮动静态路由之拨号备份<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />拨号备份提供了一种保护,使得当广域网上主干线出现故障时,启动一条备份线路,使通信正常运转. 启动备份有两种情况: 主干线断掉传输流量超过了定义的最大值…...
国产在线做a视频网站/东莞免费网站建设网络营销
偶然间在微信公众号奇舞周刊上看到了这篇文章《CSS Painting API》,算是对 conic-gradient的初次见面。 后来有空的时候,百度搜了一下,看了这篇文章《CSS神奇的conic-gradient圆锥渐变》后,对conic-gradient有了深刻的了解。 概述…...
做网站的抬头怎么做/企业排名优化公司
最近在做一功能不大、业务也不复杂的小众App,以往做App是发现自己从来没有考虑过一些架构方面的问题,只是按照自己以往的习惯去写代码,忽略了App的设计。本次分享主要包含一些开发App的小经验和技巧,来一次App开发与设计的分享。先…...