socket+netty

本文介绍了Java中的socket编程,包括长连接和短连接的概念,以及心跳包的作用。接着,文章深入探讨了Netty,解释了其基于NIO的工作原理,如BossGroup、WorkerGroup以及NioEventLoop的角色,同时提到了Pipeline处理网络事件的特点。

socket

对外提供接口的第三方库,封装了tcp/ip协议,主要接口为create,listen,accept,connect,read和write等等。

长连接短链接

所谓长连接

指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接(心跳包),一般需要自己做在线维持。

而长连接通常就是: 连接→数据传输→保持连接(心跳)→数据传输→保持连接(心跳)→……→关闭连接;

短连接

是指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接。

通常的短连接操作步骤是:连接→数据传输→关闭连接;

比如Http的,只是连接、请求、关闭,过程时间较短,服务器若是一段时间内没有收到请求即可关闭连接。其实长连接是相对于通常的短连接而说的,也就是长时间保持客户端与服务端的连接状态。

心跳包

可以指定数据,设置发送时间和发送次数

netty

基于javaNio的网络应用架构,面向缓冲区编程,组件有Channel、Buffer、Selector,

BIO

同步,阻塞io,一个客户端请求时,服务的需要提供线程处理io,这种模型对线程量的耗费极大,且线程利用率低,难以承受请求的高并发。BIO 虽然可以使用线程池+等待队列进行优化,避免使用过多的线程,但是依然无法解决线程利用率低的问题。

NIO

同步的、非阻塞式 IO,在这种模型中,服务器上一个线程处理多个连接,即多个客户端请求都会被注册到多路复用器(后文要讲的 Selector)上,多路复用器会轮训这些连接,轮训到连接上有 IO 活动就进行处理,从缓冲区读取数据的时候游标在缓冲区中是可以前后移动的,这就增加了数据处理的灵活性。这和面向流的 BIO 只能顺序读取流中数据有很大的不同。

netty工作原理

1)Netty 抽象出两组线程池:BossGroup 和 WorkerGroup,每个线程池中都有 NioEventLoop 线程。BossGroup 中的线程专门负责和客户端建立连接,WorkerGroup 中的线程专门负责处理连接上的读写。BossGroup 和 WorkerGroup 的类型都是 NioEventLoopGroup。

2)NioEventLoopGroup 相当于一个事件循环组,这个组中含有多个事件循环,每个事件循环就是一个 NioEventLoop。NioEventLoop 表示一个不断循环的执行事件处理的线程,每个 NioEventLoop 都包含一个 Selector,用于监听注册在其上的 Socket 网络连接(Channel)。

4)NioEventLoopGroup 可以含有多个线程,即可以含有多个 NioEventLoop。

5)每个 BossNioEventLoop 中循环执行以下三个步骤:

5.1)select:轮训注册在其上的 ServerSocketChannel 的 accept 事件(OP_ACCEPT 事件)

5.2)processSelectedKeys:处理 accept 事件,与客户端建立连接,生成一个 NioSocketChannel,使用自定义处理器,并将其注册到某个 WorkerNioEventLoop 上的 Selector 上

5.3)runAllTasks:再去以此循环处理任务队列中的其他任务

6)每个 WorkerNioEventLoop 中循环执行以下三个步骤:

6.1)select:轮训注册在其上的 NioSocketChannel 的 read/write 事件(OP_READ/OP_WRITE 事件)

6.2)processSelectedKeys:在对应的 NioSocketChannel 上处理 read/write 事件

6.3)runAllTasks:再去以此循环处理任务队列中的其他任务

7)在以上两个processSelectedKeys步骤中,会使用 Pipeline(管道),Pipeline 中引用了 Channel,即通过 Pipeline 可以获取到对应的 Channel,Pipeline 中维护了很多的处理器(拦截处理器、过滤处理器、自定义处理器等)。这里暂时不详细展开讲解 Pipeline。

要实现Spring Boot 3和Netty结合Socket端口,利用HTML模拟客户端发送数据,可按以下方式进行: ### 后端部分(Spring Boot 3 + Netty) #### 项目依赖 在`pom.xml`中添加必要的依赖,包含Spring Boot和Netty: ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.72.Final</version> </dependency> </dependencies> ``` #### 配置Netty服务器 创建Netty服务器的配置类,用于处理TCP连接: ```java 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.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import org.springframework.stereotype.Component; @Component public class NettyServer { private static final int PORT = 8081; public void start() throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new StringDecoder()); ch.pipeline().addLast(new StringEncoder()); ch.pipeline().addLast(new NettyServerHandler()); } }) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(PORT).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } } ``` #### 处理客户端消息 创建Netty服务器的处理器类,用于处理客户端发送的消息: ```java import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; public class NettyServerHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println("Received message: " + msg); ctx.writeAndFlush("Server received: " + msg); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } ``` #### 启动Netty服务器 在Spring Boot应用的主类中启动Netty服务器: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringBootNettyApplication implements CommandLineRunner { @Autowired private NettyServer nettyServer; public static void main(String[] args) { SpringApplication.run(SpringBootNettyApplication.class, args); } @Override public void run(String... args) throws Exception { nettyServer.start(); } } ``` ### 前端部分(HTML + JavaScript) 由于HTML本身不能直接建立TCP连接,借助JavaScript的`WebSocket`来模拟TCP连接: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>TCP Client Simulation</title> </head> <body> <input type="text" id="messageInput" placeholder="Enter message"> <button onclick="sendMessage()">Send</button> <div id="messages"></div> <script> const socket = new WebSocket('ws://localhost:8081'); socket.onopen = function () { console.log('Connected to the server'); }; socket.onmessage = function (event) { const messagesDiv = document.getElementById('messages'); messagesDiv.innerHTML += '<p>' + event.data + '</p>'; }; socket.onclose = function () { console.log('Disconnected from the server'); }; function sendMessage() { const messageInput = document.getElementById('messageInput'); const message = messageInput.value; if (message) { socket.send(message); messageInput.value = ''; } } </script> </body> </html> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值