当前位置: 首页 > news >正文

Netty篇(WebSocket)

目录

一、简介

二、特点

三、websock应用场景

四、websocket案例

1. 服务端

2. 处理器

3. 页面端处理

五、参考文献


一、简介

没有其他技术能够像WebSocket一样提供真正的双向通信,许多web开发者仍然是依赖于ajax的长轮询来

实现。(注:我认为长轮询是富于创造性和多功能性的,虽然这只是一个不太完美的解决办法(hack))

对Websocket缺少热情,也许是因为多年前他的安全性的脆弱,抑或者是缺少浏览器的支持,不管怎样,

这两个问题都已经被解决了。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动

向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。在WebSocket API中,浏览器

和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接

可以数据互相传送

二、特点

Websocket是应用层第七层上的一个应用层协议,它必须依赖HTTP协议进行一次握手 ,握手成功后,数

据就直接从TCP通道传输,与HTTP无关了。Websocket的数据传输是frame形式传输的,比如会将一条消

息分为几个frame,按照先后顺序传输出去。

这样做会有几个好处:

  • 建立在 TCP 协议之上,服务器端的实现比较容易。
  • 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手

时不容易屏蔽,能通过各种HTTP 代理服务器。

  • 数据格式比较轻量,性能开销小,通信高效。
  • 可以发送文本,也可以发送二进制数据。
  • 没有同源限制,客户端可以与任意服务器通信。
  • 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
  • 大数据的传输可以分片传输,不用考虑到数据大小导致的长度标志位不足够的情况。
  • 和http的chunk一样,可以边生成数据边传递消息,即提高传输效率。

三、websock应用场景

既然http无法满足用户的所有需求,那么为之诞生的websocket必然有其诸多应用场景。

如:

  • 实时显示网站在线人数
  • 账户余额等数据的实时更新
  • 多玩家网络游戏
  • 多媒体聊天,如:聊天室

四、websocket案例

1. 服务端

public class WebSocketServer {public static void main(String[] args) {//创建线程组  负责接收EventLoopGroup bossGroup = new NioEventLoopGroup(1);//线程组, 负责(读写)工作线程EventLoopGroup workerGroup = new NioEventLoopGroup();//创建启动类ServerBootstrap bootstrap = new ServerBootstrap();//指定启动的参数bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 128) //option指的是bossGroup里面的可选项.childOption(ChannelOption.SO_KEEPALIVE, true).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//在通道的流水线上加入一个通道处理器ch.pipeline().addLast("logging", new LoggingHandler())//设置log监听器,并且日志级别为debug,方便观察运行流程.addLast("http-codec", new HttpServerCodec())//设置解码器.addLast("aggregator", new HttpObjectAggregator(65536))//聚合器,使用websocket会用到.addLast("http-chunked", new ChunkedWriteHandler())//用于大数据的分区传输.addLast("hanlder", new WebSocketHandler());}});System.out.println("===============服务器启动了==================");try {//启动的时候绑定端口,返回ChannelFuture,异步启动ChannelFuture channelFuture = bootstrap.bind(8081).sync();//添加监听器channelFuture.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (future.isSuccess()) {System.out.println("端口: 8081启动成功");} else {System.out.println("端口: 8081启动失败");}}});channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}

2. 处理器

public class WebSocketHandler extends SimpleChannelInboundHandler<Object> {//设置通道组static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);//日志对象private final Logger logger = Logger.getLogger(this.getClass());//创建握手对象private WebSocketServerHandshaker handshaker;/*** 通道激活* @param ctx* @throws Exception*/@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {logger.debug(ctx.channel().remoteAddress()+"加入群聊");group.add(ctx.channel());}/*** 通道销毁* @param ctx* @throws Exception*/@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {logger.debug(ctx.channel().remoteAddress()+"退出群聊");group.add(ctx.channel());}/*** 服务器读处理* @param ctx* @param msg* @throws Exception*/@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {logger.debug("收到消息");if(msg instanceof FullHttpRequest){//以http请求方式接入,升级websockethandleHttpRequest(ctx, (FullHttpRequest) msg);}if(msg instanceof WebSocketFrame){//处理websocket客户端的消息handleWebSocketFrame(ctx, (WebSocketFrame) msg);}}/*** websocket处理* @param ctx* @param frame*/private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame){Channel channel = ctx.channel();if(frame instanceof CloseWebSocketFrame){handshaker.close(channel, (CloseWebSocketFrame)frame.retain());return;}if(frame instanceof PingWebSocketFrame){channel.write(new PongWebSocketFrame(frame.content().retain()));return;}if(frame instanceof TextWebSocketFrame){String text = ((TextWebSocketFrame) frame).text();logger.debug("服务端收到:"+text);TextWebSocketFrame tws = new TextWebSocketFrame(ctx.channel().remoteAddress()+"说:"+text);group.writeAndFlush(tws);}else{logger.debug("不支持非文本格式");throw new UnsupportedOperationException("不支持非文本格式"+ctx.channel().id());}}/*** http请求处理* @param ctx* @param req*/private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {//如果请求解码失败,或者不是websocketif(req.decoderResult().isFailure() || !"websocket".equals(req.headers().get("Upgrade"))){sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));return;}//创建websocket握手工厂WebSocketServerHandshakerFactory factory = new WebSocketServerHandshakerFactory("ws://localhost:8081/websocket", null, false);//创建新的握手handshaker = factory.newHandshaker(req);if(handshaker == null){//不支持WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());}else{//握手handshaker.handshake(ctx.channel(), req);}}//发送响应private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse resp){//如果响应码不是200if(resp.status().code() != 200){//创建错误状态码数据ByteBuf buf = Unpooled.copiedBuffer(resp.status().toString(), CharsetUtil.UTF_8);resp.content().writeBytes(buf);buf.release();}//回写状态码ChannelFuture channelFuture = ctx.channel().writeAndFlush(resp);if(!HttpUtil.isKeepAlive(req) || resp.status().code() != 200){channelFuture.addListener(ChannelFutureListener.CLOSE);}}}

3. 页面端处理

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<script>var socket;//判断当前浏览器是否支持websocketif(window.WebSocket) {//go onsocket = new WebSocket("ws://localhost:7000/hello2");//相当于channelReado, ev 收到服务器端回送的消息socket.onmessage = function (ev) {var rt = document.getElementById("responseText");rt.value = rt.value + "\n" + ev.data;}//相当于连接开启(感知到连接开启)socket.onopen = function (ev) {var rt = document.getElementById("responseText");rt.value = "连接开启了.."}//相当于连接关闭(感知到连接关闭)socket.onclose = function (ev) {var rt = document.getElementById("responseText");rt.value = rt.value + "\n" + "连接关闭了.."}} else {alert("当前浏览器不支持websocket")}//发送消息到服务器function send(message) {if(!window.socket) { //先判断socket是否创建好return;}if(socket.readyState == WebSocket.OPEN) {//通过socket 发送消息socket.send(message)} else {alert("连接没有开启");}}
</script><form onsubmit="return false"><textarea name="message" style="height: 300px; width: 300px"></textarea><input type="button" value="发生消息" onclick="send(this.form.message.value)"><textarea id="responseText" style="height: 300px; width: 300px"></textarea><input type="button" value="清空内容" onclick="document.getElementById('responseText').value=''"></form>
</body>
</html>

五、参考文献

  • HTML5 WebSocket

相关文章:

Netty篇(WebSocket)

目录 一、简介 二、特点 三、websock应用场景 四、websocket案例 1. 服务端 2. 处理器 3. 页面端处理 五、参考文献 一、简介 没有其他技术能够像WebSocket一样提供真正的双向通信&#xff0c;许多web开发者仍然是依赖于ajax的长轮询来 实现。&#xff08;注&#xff…...

云原生-docker安装与基础操作

一、云原生 Docker 介绍 Docker 在云原生中的优势 二、docker的安装 三、docker的基础命令 1. docker pull&#xff08;拉取镜像&#xff09; 2. docker images&#xff08;查看本地镜像&#xff09; 3. docker run&#xff08;创建并启动容器&#xff09; 4. docker ps…...

MySQL数据库:SQL语言入门 【上】(学习笔记)

SQL&#xff08;Structured Query Language&#xff09;是结构化查询语言的简称&#xff0c;它是一种数据库查询和程序设计语言&#xff0c;同时也是目前使用最广泛的关系型数据库操作语言。&#xff08;95%适用于所有关系型数据库&#xff09; 【 SQL是关系型数据库通用的操作…...

重学 Android 自定义 View 系列(六):环形进度条

目标 自定义一个环形进度条&#xff0c;可以自定义其最大值、当前进度、背景色、进度色&#xff0c;宽度等信息。 最终效果如下&#xff08;GIF展示纯色有点问题&#xff09;&#xff1a; 1. 结构分析 背景圆环&#xff1a;表示进度条的背景。进度圆环&#xff1a;表示当前…...

nodejs 020: React语法规则 props和state

props和state 在 React 中&#xff0c;props 和 state 是管理数据流的两种核心机制。理解它们之间的区别和用途是构建 React 应用程序的基础。 一、props 和 state的区别 特性propsstate定义方式由父组件传递给子组件的数据组件内部管理的本地数据是否可修改不可变&#xff…...

STM32问题集

这里写目录标题 一、烧录1、 Can not connect to target!【ST-LINK烧录】 一、烧录 1、 Can not connect to target!【ST-LINK烧录】 烧录突然 If the target is in low power mode, please enable “Debug in Low Power mode” option from Target->settings menu 然后就&…...

SwiftUI(十二)- 容器组件 布局与结构的基石

引言 在用户界面开发中&#xff0c;布局是设计一个应用程序的视觉层次和交互体验的核心之一。无论是设计简单的按钮排布&#xff0c;还是复杂的多层次页面&#xff0c;合理的布局和结构可以极大地提升用户体验。而容器组件&#xff0c;作为将多个视图整合、组织、排列的工具&a…...

想租用显卡训练自己的网络?AutoDL保姆级使用教程(PyCharm版)

各位小伙伴们大家好~ 不知道各位同学在科研过程中是否有这样的苦恼 电脑无显卡。难不成我要用CPU跑实验吗&#xff1f;救救我吧电脑显卡算力太低。训练过程慢慢慢慢慢&#xff0c;等半天都出不来结果电脑显卡显存不够&#xff0c;batchsize稍微高一点点&#xff0c;就要爆显存…...

LeetCode【0039】组合总和

本文目录 1 中文题目2 求解方法&#xff1a;回溯法2.1 方法思路2.2 Python代码2.3 复杂度分析 3 题目总结 1 中文题目 给定一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#…...

AscendC从入门到精通系列(一)初步感知AscendC

1 什么是AscendC Ascend C是CANN针对算子开发场景推出的编程语言&#xff0c;原生支持C和C标准规范&#xff0c;兼具开发效率和运行性能。基于Ascend C编写的算子程序&#xff0c;通过编译器编译和运行时调度&#xff0c;运行在昇腾AI处理器上。使用Ascend C&#xff0c;开发者…...

PostgreSQL中的COPY命令:高效数据导入与导出

在PostgreSQL数据库中&#xff0c;数据导入和导出是日常工作中常见的操作。传统的插入&#xff08;INSERT&#xff09;方法虽然可以实现数据的导入&#xff0c;但在处理大量数据时效率较低。而COPY命令则提供了一个快速、高效的方式来完成这一任务。COPY命令不仅可以用于将数据…...

【HAL库】STM32F105VCTx多通道ADC+DMA方式的【STM32CubeMX】配置及代码实现

相关代码编写 配置好后点击生成代码&#xff0c;在生成代码的adc.c文件中的初始化函数MX_ADC1_Init中添加如下代码&#xff1a; HAL_ADCEx_Calibration_Start(&hadc1); /* 校准ADC */HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC_Value,ADC_DMA_…...

[SaaS] 数禾科技 AIGC生成营销素材

https://zhuanlan.zhihu.com/p/923637935https://zhuanlan.zhihu.com/p/923637935...

vue3中查找字典列表中某个元素的值对应的列表索引值

vue3中查找字典列表中某个元素的值对应的列表索引值 目录思路方法代码实现示例解释说明 目录 思路方法 要获取字典列表中某个元素的值对应的列表索引值&#xff0c;可以使用数组的 findIndex 方法。这个方法返回数组中满足提供的测试函数的第一个元素的索引。如果没有找到&am…...

爱普生机器人EPSON RC

爱普生机器人Epson RC系列&#xff0c;搭配其专用的Epson RC编程语言和软件环境&#xff0c;为用户提供了一个直观且功能强大的机器人控制和编程解决方案。以下是对Epson RC及爱普生机器人的一些详细介绍&#xff1a; Epson RC 定义&#xff1a;Epson RC 是爱普生机器人技术中…...

Linux探秘坊-------1.系统核心的低语:基础指令的奥秘解析(1)

1.Linux的背景介绍 Linux 操作系统的发展历程充满了激情与创新喵&#xff5e;&#x1f380; 萌芽期 (1983 - 1991)&#xff1a;Linux 的历史可追溯到 1983 年&#xff0c;理查德斯托曼 (Richard Stallman) 发起 GNU 计划&#xff0c;目标是创建一个自由软件操作系统。1987 年发…...

❤React-JSX语法认识和使用

1、JSX基本使用​ JSX是React的核心 JSX是ES的扩展 jsx语法 -> 普通的JavaScript代码 -> babel React可以使用JSX的前提和原因&#xff1a; React生态系统支持&#xff1a; 脚手架通常用于构建React应用程序&#xff0c;而JSX是React框架的核心语法之一。因此&#xf…...

51单片机应用开发(进阶)---定时器应用(电子时钟)

实现目标 1、巩固定时器的配置流程&#xff1b; 2、掌握按键、数码管与定时器配合使用&#xff1b; 3、功能1&#xff1a;&#xff08;1&#xff09;简单显示时间。显示格式&#xff1a;88-88-88&#xff08;时-分-秒&#xff09; 4、功能2&#xff1a;&#xff08;1&#…...

JavaScript中的对象-栈内存和堆内存以及this指向的两种情况(后续会出进阶)

1.1 栈内存和堆内存 我们知道程序是需要加载到内存中来执行的&#xff0c;我们可以将内存划分为两个区域:栈内存和堆内存 原始类型占据的空间是在栈内存中分配的对象类型占据的空间是在堆内存中分配的 1.1.1 值类型和引用类型 原始类型的保存方式&#xff1a;在变量中保存的是…...

shell脚本使用curl上传FTP

背景&#xff1a;要求使用curl通过shell脚本实现上传文件到FTP的功能&#xff0c;同时对远程目录不存在的时候&#xff0c;主动创建目录并上传文件&#xff0c;shell脚本如下&#xff1a; #!/bin/bash# FTP服务器的地址 FTP_SERVER"ftp://1.1.1.1:2121" # FTP用户名…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

elementUI点击浏览table所选行数据查看文档

项目场景&#xff1a; table按照要求特定的数据变成按钮可以点击 解决方案&#xff1a; <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...

华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)

题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...