Netty框架介绍并编写WebSocket服务端与客户端

一、Netty 介绍

Netty 是一个基于 Java NIO(Non-blocking I/O,非阻塞 I/O)的网络通信框架,旨在帮助开发者轻松地编写高性能、高可靠性的网络应用程序。由于其出色的设计和强大的功能,Netty 在企业级应用和开源项目中得到了广泛的应用。

二、特点

2.1 高性能

Netty 使用了异步、事件驱动的编程模型,能够处理大量并发连接而不会导致线程资源的浪费。通过底层的 NIO 技术,Netty 能够实现高性能的网络通信。

2.2 可扩展性

Netty 的设计允许开发者灵活地扩展和定制各种功能。它提供了丰富的插件机制和可扩展的 API,使得开发者能够根据自己的需求定制网络应用程序。

2.3 完善的协议支持

Netty 提供了对多种网络协议的支持,包括 HTTP、WebSocket、TCP、UDP 等。开发者可以轻松地构建各种类型的网络应用程序,如 Web 服务器、实时通信应用等。

2.4 容错性和可靠性

Netty 提供了多种机制来保障网络通信的可靠性和容错性,包括心跳检测、断线重连、流量控制等。这些机制使得网络应用程序能够更加稳定地运行。

2.5 跨平台

Netty可以在多种操作系统平台上运行,包括Windows、Linux、MacOS等。

三、使用场景

3.1 高性能服务器

Netty 可以用于构建高性能的服务器,如 Web 服务器、游戏服务器等。其异步、事件驱动的设计使得服务器能够处理大量并发连接而不会导致性能下降。

3.2 实时通信应用

由于 Netty 对 WebSocket 的支持,它非常适合构建实时通信应用,如聊天应用、即时消息应用等。开发者可以利用 Netty 快速搭建稳定、高性能的实时通信系统。

3.3 分布式系统

Netty 可以作为分布式系统中的通信框架,用于实现节点之间的通信和数据传输。其可靠性和高性能使得它成为分布式系统的理想选择。

四、Netty 核心组件

1、Bootstrap(引导类):

用于启动和配置网络应用程序的配置类。ServerBootstrap 用于服务端,Bootstrap 用于客户端。


2、NioEventLoopGroup(事件循环组):

包含多个事件循环(NioEventLoop)的组件。在服务端,通常会创建两个实例:BossEventLoopGroup 负责处理连接事件,WorkerEventLoopGroup 负责处理读写事件。客户端通常只有一个 NioEventLoopGroup 处理连接和 I/O 任务。


3、NioEventLoop(事件循环):

表示一个不断循环执行事件处理的线程。每个 NioEventLoop 包含一个 Selector 和一个 TaskQueue,用于监听注册在其上的 SocketChannel 上的 I/O 事件,并处理非 I/O 任务。

4、Channel(通道):

是一种双向的连接通道,负责处理客户端与服务端之间的所有网络事件(如数据传输、生命周期事件)。不同协议和阻塞类型的连接对应不同类型的 Channel,常见的包括 NioSocketChannel(客户端 TCP Socket 连接)和 NioServerSocketChannel(服务器端 TCP Socket 连接)。

5、ChannelHandler(通道处理器):

负责处理输入输出数据的逻辑,可以接收入站事件(如数据接收)和出站事件(如数据发送),并执行相应的处理逻辑。

6、ChannelHandlerContext(通道处理器上下文):

包含与 ChannelHandler 相关联的各种信息,如 Channel、EventLoop、ChannelPipeline 等。提供了丰富的方法,便于 ChannelHandler 与其他组件进行交互。

7、ChannelPipeline(通道管道):

是一个双向链表,用于拦截和处理事件的链式结构。负责管理 ChannelHandler 并协调它们的处理顺序。每个 ChannelHandlerContext 关联着一个 ChannelHandler。

8、ChannelFuture(通道未来):

主要用于接收异步 I/O 操作返回的执行结果。提供了丰富的方法,用于检查操作状态、添加监听器以接收通知,并对操作结果进行处理。


这些组件共同构成了 Netty 的核心框架,为开发者提供了高效、可靠的网络应用开发环境。

五、使用Netty框架编写WebSocket服务端与客户端

背景:

WebSocket服务端实现功能:

简单的接收到的消息打印出来,并将接收到的消息返回给客户端

WebSocket客户端实现功能:

与服务端建立连接后,向服务端发送一条消息。并将收到服务端发送的消息打印出来

5.1、Netty服务端工作架构流程

Netty 服务端的工作架构流程可以大致描述如下:

  1. 创建引导类(Bootstrap):首先,创建一个 ServerBootstrap 实例作为服务端的启动引导类。设置引导类的配置参数,如线程模型、通道类型等。

  2. 设置线程模型:通过 group 方法设置事件循环组,一般使用 NioEventLoopGroup 类型。可以指定一个 Boss 线程组用于处理连接事件,和一个或多个 Worker 线程组用于处理读写事件。

  3. 指定通道类型和处理器:调用 channel 方法指定通道类型,如 NioServerSocketChannel。通过 childHandler 方法设置通道处理器(ChannelInitializer),用于初始化新连接的 Channel,并添加业务逻辑处理器(ChannelHandler)。

  4. 绑定端口:通过 bind 方法绑定监听端口,并调用 sync 方法等待绑定完成。

  5. 处理连接:一旦有客户端连接到服务器端口,Boss 线程就会接收到连接事件,创建新的 SocketChannel 并将其注册到 Worker 线程组的事件循环中。

  6. 事件循环处理Worker 线程会不断循环从注册的 SocketChannel 中读取数据,并将数据交给 ChannelPipeline 中的 ChannelHandler 处理。处理完成后,可以向客户端发送响应数据。

  7. 关闭通道和释放资源:当连接关闭时,可以通过 ChannelFutureListener 监听器来处理关闭事件,关闭通道并释放相关资源。

  8. 优雅关闭:最后,调用 shutdownGracefully 方法优雅地关闭 EventLoopGroup,释放所有的资源。

这是一个简单的流程,实际应用中可能还涉及到更多细节和复杂性,比如异常处理、心跳检测、流量控制等。 Netty 提供了丰富的组件和功能,可以帮助开发者构建高性能、可靠的网络应用程序。

5.2 服务端代码示例

以下是一个简单的使用Netty框架构建WebSocket服务器的示例代码

1、pom.xml文件中添加以下Netty依赖

<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.68.Final</version>
</dependency>

2、WebSocketServerHandler是一个自定义的处理器

WebSocketServerHandler是一个自定义的处理器,用于处理WebSocket相关的事件。在上述示例代码中,我们添加了一个WebSocketServerHandler类来处理WebSocket连接和消息的处理逻辑。

以下是WebSocketServerHandler类的简单示例:

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;public class WebSocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {System.out.println("服务器接受消息中...");// 处理接收到的WebSocket消息String request = msg.text();System.out.println("服务器接受消息中...");System.out.println("Received WebSocket message: " + request);// 假设这里的业务逻辑是简单地将接收到的消息返回给客户端ctx.channel().writeAndFlush(new TextWebSocketFrame("Server received: " + request));}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {// 发生异常时关闭连接cause.printStackTrace();ctx.close();}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {System.out.println("Client connected: " + ctx.channel().remoteAddress());super.channelActive(ctx);}
}

1、WebSocketServerHandler继承自Netty的SimpleChannelInboundHandler类,并指定处理的消息类型为TextWebSocketFrame,这表示它将处理来自客户端的文本类型的WebSocket帧。

在channelRead0()方法中,我们处理接收到的WebSocket消息。在这个简单的示例中,我们只是打印接收到的消息,并将其原样返回给客户端。

在exceptionCaught()方法中,我们处理发生异常时的情况。在这里,我们简单地打印异常信息,并关闭与客户端的连接。

可以根据实际需求来编写自定义的WebSocketServerHandler,以实现特定的业务逻辑。

2、在 Netty 中,ChannelHandlerContext 是一个上下文对象,代表了 ChannelHandler 和 ChannelPipeline 之间的关联。它包含了当前 ChannelHandler 的上下文信息以及与其相关联的 Channel、EventLoop、ChannelPipeline 等。通过 ChannelHandlerContext,你可以获取当前处理器的相关信息,并与其他处理器、Channel 进行交互。

在你提供的代码中,ChannelHandlerContext 被用于处理接收到的 WebSocket 消息。具体来说,你可以通过 ctx 来执行以下操作:

  • 获取当前 Channel:通过 ctx.channel() 方法获取当前的 Channel 对象,你可以通过它进行消息的发送和连接的管理。
  • 获取关联的 ChannelPipeline:通过 ctx.pipeline() 方法获取当前的 ChannelPipeline 对象,你可以对其进行添加、删除和修改处理器等操作。
  • 获取关联的 EventLoop:通过 ctx.executor() 方法获取当前的 EventExecutor(一般为 EventLoop),你可以将任务提交给它来异步执行。
  • 发送消息:通过 ctx.writeAndFlush() 方法向当前连接的客户端发送消息。

总之,ChannelHandlerContext 提供了与当前处理器相关的一切信息,以便你在处理消息时能够方便地与其他组件进行交互。

3、服务端WebSocketServer代码

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;public class WebSocketServer {private final int port = 8080;public void run() throws Exception {// 创建两个EventLoopGroup,一个用于接收客户端连接,另一个用于处理客户端连接的IO操作EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();System.out.println("test1");try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // 指定使用NIO传输.childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {// 添加HTTP编解码器ch.pipeline().addLast(new HttpServerCodec());// 添加HTTP对象聚合器,将HTTP消息的多个部分合成一条完整的HTTP消息ch.pipeline().addLast(new HttpObjectAggregator(65536));// 添加WebSocket协议处理器,将HTTP协议升级为WebSocket协议ch.pipeline().addLast(new WebSocketServerProtocolHandler("/websocket"));// 添加自定义的WebSocket处理器ch.pipeline().addLast(new WebSocketServerHandler());}}).option(ChannelOption.SO_BACKLOG, 128) // 设置TCP参数.childOption(ChannelOption.SO_KEEPALIVE, true); // 设置TCP参数System.out.println("test3");// 绑定端口,开始接收进来的连接ChannelFuture f = b.bind(port).sync();System.out.println("test4");// 等待服务器套接字关闭f.channel().closeFuture().sync();} finally {// 优雅地关闭EventLoopGroup,释放所有的资源workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();System.out.println("test2");}}public static void main(String[] args) throws Exception {System.out.println("test0");WebSocketServer server = new WebSocketServer();server.run();}
}

该示例代码中使用了Netty框架来构建WebSocket服务器。主要步骤包括创建两个EventLoopGroup(用于接收客户端连接和处理客户端连接的IO操作)、配置ServerBootstrap(设置服务器参数,如传输类型、处理器)、设置ChannelInitializer(初始化通道,添加处理器链)以及绑定端口并启动服务器。

5.3 WebSocket 客户端架构流程

当使用 Netty 构建 WebSocket 客户端时,通常会遵循以下流程:

  1. 创建 EventLoopGroup:EventLoopGroup 是用于处理 I/O 事件的线程池。通常会创建一个 NioEventLoopGroup 对象,它包含了一组 NIO 线程,用于处理连接、读取、写入等操作。

  2. 配置 Bootstrap:Bootstrap 是 Netty 的启动类,用于设置客户端的引导配置。通过 Bootstrap 对象,我们可以设置 EventLoopGroup、Channel 类型以及 ChannelHandler 等属性。

  3. 创建并配置 ChannelInitializer:ChannelInitializer 是一个特殊的 ChannelHandler,用于初始化 Channel 的管道。在 ChannelInitializer 中,我们可以添加一系列的 ChannelHandler,在初始化过程中将它们添加到 ChannelPipeline 中。

  4. 创建 WebSocketClientHandshaker:WebSocketClientHandshaker 是用于进行握手的类。它提供了一些参数,如 URI、WebSocket 版本、自定义扩展等,用于构建握手请求。

  5. 连接到 WebSocket 服务器:通过调用 Bootstrap 的 connect 方法,传入服务器的主机和端口,与 WebSocket 服务器建立连接。连接成功后,会返回一个 ChannelFuture 对象,可以通过该对象获取与服务器的 Channel。

  6. 设置 WebSocketClientHandshaker:从 Channel 中获取 WebSocketClientHandler,并将 WebSocketClientHandshaker 设置到 WebSocketClientHandler 中。这样,WebSocketClientHandler 就可以使用 WebSocketClientHandshaker 进行握手操作。

  7. 发起握手请求:通过调用 WebSocketClientHandshaker 的 handshake 方法,发起握手请求。握手请求会发送到服务器,并等待服务器的响应。

  8. 连接建立成功后,可以发送消息到 WebSocket 服务器:在握手成功后,可以通过 Channel 向 WebSocket 服务器发送消息。消息需要封装为合适的 WebSocketFrame,例如 TextWebSocketFrame 或 BinaryWebSocketFrame。

  9. 处理服务器返回的消息:WebSocketClientHandler 会接收到服务器返回的消息,可以在其中处理和解析服务器发送过来的消息。

  10. 关闭连接:当不再需要与服务器通信时,可以调用 Channel 的 close 方法关闭连接。同时,需要调用 EventLoopGroup 的 shutdownGracefully 方法来优雅地关闭 EventLoopGroup。

以上是 Netty WebSocket 客户端的基本工作架构流程。通过合适的配置和处理逻辑,我们可以构建出强大而高效的 WebSocket 客户端应用程序。

注意:

不要在客户端的 channelActive 方法中发送消息,WebSocketClientProtocolHandler 不支持 Active,使用 channelActive 发送消息没有响应。发送消息需要在客户端启动后,通过异步的方式。小心踩坑

5.4 WebSocket 客户端代码

1、依赖

    <dependencies><!-- Netty dependencies --><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.68.Final</version></dependency></dependencies>

2、代码示例

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;import java.net.URI;
import java.util.concurrent.CountDownLatch;public class WebSocketClient {static final CountDownLatch latch = new CountDownLatch(1); // 用于阻塞主线程,直到握手完成public static void main(String[] args) throws Exception {// 目标 WebSocket 地址String url = "ws://127.0.0.1:8080/websocket";WebSocketClient client = new WebSocketClient();client.test(url);}public void test(String url) throws Exception {Channel dest = dest(url); // 获取目标通道latch.await(); // 等待握手完成dest.writeAndFlush(new TextWebSocketFrame("我就是要发送的消息2")); // 发送消息}public Channel dest(String url) throws Exception {final URI webSocketURL = new URI(url); // 目标 WebSocket 地址EventLoopGroup group = new NioEventLoopGroup(); // 创建事件循环组Bootstrap boot = new Bootstrap(); // 创建引导程序boot.option(ChannelOption.SO_KEEPALIVE, true).option(ChannelOption.TCP_NODELAY, true).group(group).handler(new LoggingHandler(LogLevel.INFO)) // 添加日志处理器,用于打印日志信息.channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {protected void initChannel(SocketChannel sc) throws Exception {ChannelPipeline pipeline = sc.pipeline();pipeline.addLast(new HttpClientCodec()); // HTTP 客户端编解码器,用于处理 HTTP 请求和响应pipeline.addLast(new ChunkedWriteHandler()); // 支持大数据流写入pipeline.addLast(new HttpObjectAggregator(64 * 1024)); // 聚合 HTTP 消息,将多个消息合并成一个完整的 FullHttpRequest 或 FullHttpResponse// WebSocket 客户端协议处理器,用于处理 WebSocket 握手和帧的编解码pipeline.addLast(new WebSocketClientProtocolHandler(WebSocketClientHandshakerFactory.newHandshaker(webSocketURL, WebSocketVersion.V13, null, false, new DefaultHttpHeaders())));pipeline.addLast(new WebSocketClientHandler());}});ChannelFuture cf = boot.connect(webSocketURL.getHost(), webSocketURL.getPort()).sync(); // 连接到目标 WebSocket 服务器return cf.channel(); // 返回通道}public static void send(Channel channel) {final String textMsg = "握手完成后发送的消息"; // 要发送的消息内容if (channel != null && channel.isActive()) {TextWebSocketFrame frame = new TextWebSocketFrame(textMsg); // 创建 WebSocket 文本帧channel.writeAndFlush(frame).addListener((ChannelFutureListener) channelFuture -> {if (channelFuture.isDone() && channelFuture.isSuccess()) {System.out.println("     ================= 发送成功.");} else {channelFuture.channel().close();System.out.println("     ================= 发送失败. cause = " + channelFuture.cause());channelFuture.cause().printStackTrace();}});} else {System.out.println("消息发送失败! textMsg = " + textMsg);}}// WebSocket客户端处理器public static class WebSocketClientHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {// 当从服务器接收到消息时调用@Overrideprotected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {System.out.println(" 客户端收到消息======== " + msg.text());}@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if (WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE.equals(evt)) {System.out.println(ctx.channel().id().asShortText() + " 握手完成!");latch.countDown(); // 计数减一,握手完成send(ctx.channel()); // 发送消息}super.userEventTriggered(ctx, evt);}// 当通道不活动时调用@Overridepublic void channelInactive(ChannelHandlerContext ctx) {System.out.println("channelInactive");}}
}

final CountDownLatch latch = new CountDownLatch(1); // 用于阻塞主线程,直到握手完成。

这行代码创建了一个 CountDownLatch 对象,该对象用于在主线程中阻塞,直到计数器减少到零。在这里,计数器的初始值为 1,表示需要等待一个事件发生。主线程在 latch.await() 被调用时会被阻塞,直到另一个线程调用了 latch.countDown() 来减少计数器的值,使得计数器为零,主线程才会继续执行。在这个代码中,主线程需要等待 WebSocket 握手完成后才能继续执行,因此使用 CountDownLatch 来实现等待握手完成的功能。

上述示例代码仅用于演示 Netty 框架构建 WebSocket 客户端的基本流程,实际应用中可能还需要考虑异常处理、心跳机制等更复杂的逻辑。

六、相关内容:jmeter压测WebSocket协议

jmeter压测websocket协议-CSDN博客

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

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

相关文章

智慧安防系统EasyCVR视频汇聚平台接入大华设备无法语音对讲的原因排查与解决

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台支持7*24小时实时高清视频监控&#xff0c;能同时播放多路监控视频流&#xff0c;视频画面1、4、9、16个可选&#xff0c;支持自定义视频轮播。EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标…

不同的子序列-java

题目描述(力扣题库115): 给你两个字符串 s 和 t &#xff0c;统计并返回在 s 的 子序列 中 t 出现的个数&#xff0c;结果需要对 109 7 取模。 示例 1&#xff1a; 输入&#xff1a;s "rabbbit", t "rabbit"输出&#xff1a;3 解释&#xff1a; 如下所示…

小型企业网络安全指南

许多小型企业刚刚起步&#xff0c;没有大公司所拥有的相同资源来保护其数据。他们不仅可能没有资金来支持多样化的安全计划&#xff0c;而且也可能没有人力或时间。 网络犯罪分子知道小型企业缺乏这些资源&#xff0c;并利用这些资源来谋取利益。遭受网络攻击后&#xff0c;小…

hyperf websocket

composer require hyperf/websocket-server 配置 Server 修改 config/autoload/server.php&#xff0c;增加以下配置。 <?phpreturn [servers > [[name > ws,type > Server::SERVER_WEBSOCKET,host > 0.0.0.0,port > 9502,sock_type > SWOOLE_SOCK_TCP…

2024年河北省网络建设与运维-省赛-nginx 和tomcat 服务服务步骤

题目&#xff1a; 5.nginx 和tomcat 服务 任务描述&#xff1a;利用系统自带tomcat&#xff0c;搭建 Tomcat网站。 &#xff08;1&#xff09;配置 linux2 为 nginx 服务器&#xff0c;网站目录为/www/nginx&#xff0c;默认文档 index.html 的内容为“HelloNginx”&#xf…

Rust与Go的对比

在各个领域&#xff0c;Rust 都已经成为一流的语言。最近&#xff0c;我们通过将服务的实现从 Go 切换到 Rust&#xff0c;极大地提升了该服务的性能。这里我阐述了重新实现服务为何是有价值的、该过程是如何实现的以及由此带来的性能提升。 Read States 服务 我们从 Go 切换…

01 Php学习:导学篇

Php是什么&#xff1f; PHP 是服务器端脚本语言。 PHP&#xff08;Hypertext Preprocessor&#xff09;是一种通用开源脚本语言&#xff0c;主要用于服务器端开发。PHP脚本在服务器端执行&#xff0c;生成动态网页内容或执行服务器端任务。PHP可以嵌入到HTML中&#xff0c;也…

千视携 NDI 6 轻量化媒体方案亮相北京CCBN展会

展会简介 第30届中国国际广播电视网络技术展览会&#xff08;CCBN&#xff09;将于4月24至26日在北京首钢会展中心举行。此次展会将汇集全球各大数字媒体、广播电视单位以及IT、通信技术厂商。展会重点关注数字化转型、智能媒体、融媒体等主题&#xff0c;并展示最新的5G、4K/8…

MongoDB聚合运算符:$min

文章目录 语法使用空值和缺失值的处理数组操作数的处理 举例在$group阶段使用在$setWindowFields阶段使用在$project阶段使用 $min聚合运算符用于返回最小值。$min对于不同的类型的值使用BSON的比较顺序。 $min可以用于下面的这些阶段&#xff1a; $addFields$bucket$bucketA…

【运输层】TCP 的可靠传输是如何实现的?

目录 1、发送和接收窗口&#xff08;滑动窗口&#xff09; &#xff08;1&#xff09;滑动窗口的工作流程 &#xff08;2&#xff09;滑动窗口和缓存的关系 &#xff08;3&#xff09;滑动窗口的注意事项 2、如何选择超时重传时间 &#xff08;1&#xff09;加权平均往返…

5.网络编程-socker(golang版)

目录 一、什么是socket&#xff1f; 二、Golang中使用TCP TCP服务端 TCP客户端​​​​​​​ 三、TCP黏包&#xff0c;拆包 1.什么是粘包&#xff0c;拆包&#xff1f; 2.为什么UDP没有粘包&#xff0c;拆包&#xff1f; 3.粘包拆包发生场景 4.TCP黏包 黏包服务端 …

解决 IDEA每次打开新的项目都要重新设置maven问题

目录 一、当前项目设置maven 如下图&#xff1a; 二、设置打开的新项目的maven 如下图&#xff1a;​ 一、当前项目设置maven 对于当前项目我们都知道设置maven的配置要在 File -- Settings -- Build -- Maven 中设置 如下图&#xff1a; 二、设置打开的新项目的maven F…

Java编程题 | 数组逆序输出

大家可以关注一下专栏&#xff0c;方便大家需要的时候直接查找&#xff0c;专栏将持续更新~ 题目描述 编写一个Java程序&#xff0c;用于接收一个整数数组作为输入&#xff0c;然后逆序输出这个数组中的所有元素。 程序需要接收一个整数数组作为输入&#xff0c;然后输…

整理的微信小程序日历(单选/多选/筛选)

一、日历横向多选&#xff0c;支持单日、双日、三日、工作日等选择 效果图 wxml文件 <view class"calendar"><view class"section"><view class"title flex-box"><button bindtap"past">上一页</button&…

初步了解Windows作业对象

Windows提供了一个作业(job)内核对象,它允许你将进程组合在一起; 起到类似沙箱的作用; 可以对作业中的进程施加平时不能施加的限制; 这程序是《Windows核心编程》中的,我只初略看了一下, #include "stdafx.h" #include "windows.h" #include "…

stm32与esp8266WIFI模块

硬件介绍 WIFI模块ESP-01S 使用AT指令控制1-ESP8266-AT指令初试化及部分基础知识_ch_pd-CSDN博客 项目需求 通过ESP-01SWIFI模块控制LED状态模拟插座 串口1用于与ESP8266通讯&#xff0c;串口2连接PC&#xff0c;用于打印log&#xff0c;查看系统状态 项目接线 将WIFI模块的…

关于在Ubuntu上配置mysql踩的一些坑

最近准备换工作了&#xff0c;回顾了下学校时期做的那个webserver&#xff0c;又在linux下mysql踩了一些坑&#xff0c;特此记录下来 程序编译错误mysql.h: No such file or directory 云服务器缺少mysql必要的运行组件&#xff0c;安装&#xff1a; sudo apt-get install l…

智慧农场物联网系统:重塑农业的未来

随着科技的进步&#xff0c;物联网技术正在逐渐改变我们的生活。在农业领域&#xff0c;物联网系统也正在发挥着越来越重要的作用&#xff0c;为智慧农场的发展提供了新的可能。本文将深入探讨智慧农场物联网系统的优势、应用场景、技术实现以及未来发展趋势。 一、智慧农场物…

ATAM方法架构评估实践

用ATAM方法评估软件体系结构&#xff0c;其工作分为4个基本阶段&#xff0c;即演示、调查和分析、测试和报告ATAM&#xff08;如图1所示&#xff09;。接下来分别就每个阶段的实践进行详细介绍。 图1 ATAM方法的评估实践阶段划分 1.阶段1——演示&#xff08;Presentation&…

Android14音频进阶之<进阶调试>:Perfetto定位系统音频问题(六十六)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 优质视频课程:AAOS车载系统+AOSP…