军棋是哪个网站开发的,确定网站开发团队,wordpress无法缩进,公众号小程序商店一、netty介绍 Netty 是一个基于NIO的客户、服务器端的编程框架#xff0c;使用Netty 可以确保你快速和简单的开发出一个网络应用#xff0c;例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程#xff0c;例如#xff1a;基于TCP和U… 一、netty介绍 Netty 是一个基于NIO的客户、服务器端的编程框架使用Netty 可以确保你快速和简单的开发出一个网络应用例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程例如基于TCP和UDP的socket服务开发。快速”和“简单”并不用产生维护性或性能上的问题。Netty 是一个吸收了多种协议包括FTP、SMTP、HTTP等各种二进制文本协议的实现经验并经过相当精心设计的项目。最终Netty 成功的找到了一种方式在保证易于开发的同时还保证了其应用的性能稳定性和伸缩性 二、工程搭建 实验目标实现推送消息给指定的用户 pom.xml ?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsdparentartifactIdspringboot-demo/artifactIdgroupIdcom.et/groupIdversion1.0-SNAPSHOT/version/parentmodelVersion4.0.0/modelVersionartifactIdnetty/artifactIdpropertiesmaven.compiler.source8/maven.compiler.sourcemaven.compiler.target8/maven.compiler.target/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-autoconfigure/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdio.netty/groupIdartifactIdnetty-all/artifactIdversion4.1.87.Final/version/dependencydependencygroupIdcn.hutool/groupIdartifactIdhutool-all/artifactIdversion5.6.1/version/dependency/dependencies
/project 属性文件 server:port: 8088 netty server package com.et.netty.server;import com.et.netty.config.ProjectInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.net.InetSocketAddress;/*** author dongliang7* projectName websocket-parent* ClassName NettyServer.java* description: TODO* createTime 2023年02月06日 16:41:00*/
Component
public class NettyServer {static final Logger log LoggerFactory.getLogger(NettyServer.class);/*** 端口号*/Value(${webSocket.netty.port:8889})int port;EventLoopGroup bossGroup;EventLoopGroup workGroup;AutowiredProjectInitializer nettyInitializer;PostConstructpublic void start() throws InterruptedException {new Thread(() - {bossGroup new NioEventLoopGroup();workGroup new NioEventLoopGroup();ServerBootstrap bootstrap new ServerBootstrap();// bossGroup辅助客户端的tcp连接请求, workGroup负责与客户端之前的读写操作bootstrap.group(bossGroup, workGroup);// 设置NIO类型的channelbootstrap.channel(NioServerSocketChannel.class);// 设置监听端口bootstrap.localAddress(new InetSocketAddress(port));// 设置管道bootstrap.childHandler(nettyInitializer);// 配置完成开始绑定server通过调用sync同步方法阻塞直到绑定成功ChannelFuture channelFuture null;try {channelFuture bootstrap.bind().sync();log.info(Server started and listen on:{}, channelFuture.channel().localAddress());// 对关闭通道进行监听channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();}}).start();}/*** 释放资源*/PreDestroypublic void destroy() throws InterruptedException {if (bossGroup ! null) {bossGroup.shutdownGracefully().sync();}if (workGroup ! null) {workGroup.shutdownGracefully().sync();}}
} ProjectInitializer 初始化设置websocket handler package com.et.netty.config;import com.et.netty.handler.WebSocketHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;/*** author dongliang7* projectName websocket-parent* ClassName ProjectInitializer.java* description: 管道配置* createTime 2023年02月06日 16:43:00*/
Component
public class ProjectInitializer extends ChannelInitializerSocketChannel {/*** webSocket协议名*/static final String WEBSOCKET_PROTOCOL WebSocket;/*** webSocket路径*/Value(${webSocket.netty.path:/webSocket})String webSocketPath;AutowiredWebSocketHandler webSocketHandler;Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {// 设置管道ChannelPipeline pipeline socketChannel.pipeline();// 流水线管理通道中的处理程序Handler用来处理业务// webSocket协议本身是基于http协议的所以这边也要使用http编解码器pipeline.addLast(new HttpServerCodec());pipeline.addLast(new ObjectEncoder());// 以块的方式来写的处理器pipeline.addLast(new ChunkedWriteHandler());pipeline.addLast(new HttpObjectAggregator(8192));pipeline.addLast(new WebSocketServerProtocolHandler(webSocketPath, WEBSOCKET_PROTOCOL, true, 65536 * 10));// 自定义的handler处理业务逻辑pipeline.addLast(webSocketHandler);}
} WebSocketHandler package com.et.netty.handler;import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.et.netty.config.NettyConfig;
import com.et.netty.server.NettyServer;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;/*** author dongliang7* projectName websocket-parent* ClassName WebSocketHandler.java* description: TODO* createTime 2023年02月06日 16:44:00*/
Component
ChannelHandler.Sharable
public class WebSocketHandler extends SimpleChannelInboundHandlerTextWebSocketFrame {private static final Logger log LoggerFactory.getLogger(NettyServer.class);/*** 一旦连接第一个被执行*/Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {log.info(有新的客户端链接[{}], ctx.channel().id().asLongText());// 添加到channelGroup 通道组NettyConfig.getChannelGroup().add(ctx.channel());}/*** 读取数据*/Overrideprotected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {log.info(服务器收到消息{}, msg.text());// 获取用户ID,关联channelJSONObject jsonObject JSONUtil.parseObj(msg.text());String uid jsonObject.getStr(uid);NettyConfig.getChannelMap().put(uid, ctx.channel());// 将用户ID作为自定义属性加入到channel中方便随时channel中获取用户IDAttributeKeyString key AttributeKey.valueOf(userId);ctx.channel().attr(key).setIfAbsent(uid);// 回复消息ctx.channel().writeAndFlush(new TextWebSocketFrame(服务器收到消息啦));}Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {log.info(用户下线了:{}, ctx.channel().id().asLongText());// 删除通道NettyConfig.getChannelGroup().remove(ctx.channel());removeUserId(ctx);}Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {log.info(异常{}, cause.getMessage());// 删除通道NettyConfig.getChannelGroup().remove(ctx.channel());removeUserId(ctx);ctx.close();}/*** 删除用户与channel的对应关系*/private void removeUserId(ChannelHandlerContext ctx) {AttributeKeyString key AttributeKey.valueOf(userId);String userId ctx.channel().attr(key).get();NettyConfig.getChannelMap().remove(userId);}
} NettyConfig package com.et.netty.config;import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;import java.util.concurrent.ConcurrentHashMap;/*** author dongliang7* projectName websocket-parent* ClassName NettyConfig.java* description: 管理全局Channel以及用户对应的channel推送消息* createTime 2023年02月06日 16:43:00*/
public class NettyConfig {/*** 定义全局单利channel组 管理所有channel*/private static volatile ChannelGroup channelGroup null;/*** 存放请求ID与channel的对应关系*/private static volatile ConcurrentHashMapString, Channel channelMap null;/*** 定义两把锁*/private static final Object lock1 new Object();private static final Object lock2 new Object();public static ChannelGroup getChannelGroup() {if (null channelGroup) {synchronized (lock1) {if (null channelGroup) {channelGroup new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);}}}return channelGroup;}public static ConcurrentHashMapString, Channel getChannelMap() {if (null channelMap) {synchronized (lock2) {if (null channelMap) {channelMap new ConcurrentHashMap();}}}return channelMap;}public static Channel getChannel(String userId) {if (null channelMap) {return getChannelMap().get(userId);}return channelMap.get(userId);}
} controller package com.et.netty.controller;import com.et.netty.service.PushMsgService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** author dongliang7* projectName* ClassName TestController.java* description: TODO* createTime 2023年02月06日 17:48:00*/
RestController
RequestMapping(/push)
public class TestController {AutowiredPushMsgService pushMsgService;/*** 推送消息到具体客户端* param uid*/GetMapping(/{uid})public void pushOne(PathVariable String uid) {pushMsgService.pushMsgToOne(uid, hello-------------------------);}/*** 推送消息到所有客户端*/GetMapping(/pushAll)public void pushAll() {pushMsgService.pushMsgToAll(hello all-------------------------);}
} PushMsgService package com.et.netty.service;/*** author dongliang7* projectName websocket-parent* ClassName PushMsgService.java* description: 推送消息接口* createTime 2023年02月06日 16:44:00*/
public interface PushMsgService {/*** 推送给指定用户*/void pushMsgToOne(String userId, String msg);/*** 推送给所有用户*/void pushMsgToAll(String msg);
} PushMsgServiceImpl package com.et.netty.service;import com.et.netty.config.NettyConfig;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.springframework.stereotype.Service;import java.util.Objects;/*** author dongliang7* projectName websocket-parent* ClassName PushMsgServiceImpl.java* description: 推送消息实现类* createTime 2023年02月06日 16:45:00*/
Service
public class PushMsgServiceImpl implements PushMsgService {Overridepublic void pushMsgToOne(String userId, String msg) {Channel channel NettyConfig.getChannel(userId);if (Objects.isNull(channel)) {throw new RuntimeException(未连接socket服务器);}channel.writeAndFlush(new TextWebSocketFrame(msg));}Overridepublic void pushMsgToAll(String msg) {NettyConfig.getChannelGroup().writeAndFlush(new TextWebSocketFrame(msg));}
} 文章值贴出部分关键代码具体的详情代码参加代码仓库的netty模块 代码仓库 https://github.com/Harries/springboot-demo 三、测试 启动springboot工程 2024-03-08 11:21:32.975 INFO 10348 --- [ Thread-2] com.et.netty.server.NettyServer : Server started and listen on:/0:0:0:0:0:0:0:0:8889 postman创建websocket连接 ws://127.0.0.1:8889/webSocket并发送消息{uid:sss}给服务端打开浏览器给用户sss推送消息 http://127.0.0.1:8088/push/sss 四、引用 https://www.cnblogs.com/dongl961230/p/17099057.htmlhttp://www.liuhaihua.cn/archives/710299.html