Netty学习——实战篇4 Netty开发Http服务实战、ByteBuf使用、开发群聊系统 备份

本文主要是介绍Netty学习——实战篇4 Netty开发Http服务实战、ByteBuf使用、开发群聊系统 备份,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 1 Netty开发Http服务实战

        (1)Netty服务器监听8000端口,浏览器发出请求“http://localhost:8000”

        (2)服务器可以回复消息给客户端,“你好,我是服务器”,并对特定请求资源进行过滤。

HttpServer.java
public class HttpServer {public static void main(String[] args) throws Exception{//1 创建bossGroup和workerGroup线程组NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);NioEventLoopGroup workerGroup = new NioEventLoopGroup();try {//创建ServerBootstrap对象ServerBootstrap serverBootstrap = new ServerBootstrap();//配置ServerBootstrap对象serverBootstrap.group(bossGroup,workerGroup) // 设置两个线程组.channel(NioServerSocketChannel.class) //使用NIOServerSocketChannel作为服务端的通道.childHandler(new ServerInitializer()); //设置Handler//绑定端口并异步启动ChannelFuture channelFuture = serverBootstrap.bind(8000).sync();// 监听 关闭通道事件channelFuture.channel().closeFuture().sync();}finally {//关闭线程组bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}
HttpServerHandler.java
/*SimpleChannelInboundHandler 是 ChannelInboundHandlerAdapter的子类HttpObject 封装了客户端和服务端相互通讯的数据*/
@Slf4j
public class HttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {//读取客户端数据@Overrideprotected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {log.info("对应的channel是:{},pipeline是:{},通过pipeline获取channel是:{}",ctx.channel(),ctx.pipeline(),ctx.pipeline().channel());log.info("当前ctx的handler是:{}",ctx.handler());//获取//判断 msg 是不是HttpRequest请求if(msg instanceof HttpRequest){log.info("ctx类型是:{}",ctx.getClass());log.info("pipeline的hashcode是:{},HttpServerHandler的 hashcode是:",ctx.pipeline().hashCode(),this.hashCode());log.info("msg类型是:{}",msg.getClass());log.info("客户端地址是:{}",ctx.channel().remoteAddress());//获取httpRequestHttpRequest request = (HttpRequest) msg;//获取uriString uri = request.uri();//过滤指定的资源if("/favicon.ico".equals(uri)){log.info("请求了 favicon.ico, 不做响应");return;}//回复信息到浏览器(http协议)ByteBuf content = Unpooled.copiedBuffer("hello,我是服务器",CharsetUtil.UTF_8);//构造一个http的响应FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,content);response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());//把构建好的response返回ctx.writeAndFlush(response);}}
}
ServerInitializer.java
@Slf4j
public class ServerInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//向管道加入处理器//获取管道ChannelPipeline pipeline = ch.pipeline();//加入一个netty 提供的httpServerCodec codec =>[coder - decoder]//HttpServerCodec 说明//1. HttpServerCodec 是netty 提供的处理http的 编-解码器pipeline.addLast("MyHttpServerCodec",new HttpServerCodec());//2  加入自定义的handlerpipeline.addLast("MyHandler",new HttpServerHandler());log.info("服务初始化完成");}
}

2 ByteBuf实战

ByteBuf01.java,使用 Unpooled.buffer()方法创建ByteBuf对象

public class ByteBuf01 {public static void main(String[] args) {ByteBuf buffer = Unpooled.buffer(10);//写入数据for (int i = 0; i < 10; i++) {buffer.writeByte(i);}System.out.println("buffer的容量是= "+ buffer.capacity());System.out.println("buffer的readerIndex = "+ buffer.readerIndex());System.out.println("buffer的writerIndex = "+ buffer.writerIndex());//读取数据for (int i = 0; i < buffer.capacity(); i++) {System.out.print(buffer.readByte() + " ");}System.out.println();System.out.println("buffer的容量是= "+ buffer.capacity());System.out.println("buffer的readerIndex = "+ buffer.readerIndex());System.out.println("buffer的writerIndex = "+ buffer.writerIndex());}
}

运行结果:

NettyByteBuf02.java.使用 Unpooled.copiedBuffer()创建ByteBuf对象

@Slf4j
public class ByteBuf02 {public static void main(String[] args) {ByteBuf byteBuf = Unpooled.copiedBuffer("你好,世界!", Charset.forName("utf-8"));if(byteBuf.hasArray()){//把ByteBuf转换成byte[]byte[] content = byteBuf.array();//把byte[]转换成字符串String result = new String(content, Charset.forName("utf-8"));log.info("result = {}",result);log.info("偏移量 = {}",byteBuf.arrayOffset());log.info("readerIndex = {}",byteBuf.readerIndex());log.info("writerIndex = {}",byteBuf.writerIndex());log.info("capacity = {}",byteBuf.capacity());for (int i = 0; i < byteBuf.readableBytes(); i++) {log.info("内容是:{}",byteBuf.readByte());}log.info("result = {}",result);log.info("偏移量 = {}",byteBuf.arrayOffset());log.info("readerIndex = {}",byteBuf.readerIndex());log.info("writerIndex = {}",byteBuf.writerIndex());log.info("capacity = {}",byteBuf.capacity());}}
}

运行结果

3 群聊系统

3.1 需求

        (1)编写一个Netty群聊系统,实现服务器和客户端之间的数据简单通讯(非阻塞)

        (2)实现多人群聊

        (3)服务端:可以监测用户上线,离线,并实现消息转发功能

        (4)客户端:通过channel可以无阻塞发送消息给其他用户,同时可以接收其他用户发送的消息(通过服务器转发获取)

3.2 服务器代码

NettyChatServer.java

@Slf4j
public class NettyChatServer {//监听端口private int port;public NettyChatServer(int port) {this.port = port;}public void run(){//创建两个线程组 bossGroup和workerGroupNioEventLoopGroup bossGroup = new NioEventLoopGroup(1);NioEventLoopGroup workerGroup = new NioEventLoopGroup();try {//创建ServerBootstrap对象ServerBootstrap serverBootstrap = new ServerBootstrap();//设置serverBootstrapserverBootstrap.group(bossGroup,workerGroup)  //设置bossGroup和workerGroup.channel(NioServerSocketChannel.class) //使用NIOServerSocketChannel作为服务端的通道.option(ChannelOption.SO_BACKLOG,128) // 设置bossGroup 最大连接数量.childOption(ChannelOption.SO_KEEPALIVE,true) //设置workerGroup 保持活动状态.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//获取pipelineChannelPipeline pipeline = ch.pipeline();//向pipeline加入解码器pipeline.addLast("decoder",new StringDecoder());//向pipeline加入编码器pipeline.addLast("encoder",new StringEncoder());//向pipeline加入自定义handlerpipeline.addLast(new NettyChatServerHandler());}});log.info("服务器启动成功");//绑定端口并启动ChannelFuture channelFuture = serverBootstrap.bind(port).sync();channelFuture.channel().closeFuture().sync();}catch (Exception e){e.printStackTrace();}finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}public static void main(String[] args) {new NettyChatServer(8000).run();}
}

3.3 服务端Handler

NettyChatServerHandler.java

@Slf4j
public class NettyChatServerHandler extends SimpleChannelInboundHandler<String> {//创建通道组,管理所有连接的通道。GlobalEventExecutor.INSTANCE 是一个全局事件执行器,为单例private static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);//日期SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//当有新的连接时,执行该方法@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {//获取通道Channel channel = ctx.channel();//通知:客户端上线ctx.writeAndFlush("[客户端]" + channel.remoteAddress() + "在" + sdf.format(new Date()) + "加入聊天");//把通道加入通道组channels.add(channel);}//当连接断开时,执行该方法@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {Channel channel = ctx.channel();ctx.writeAndFlush("[客户端]" + channel.remoteAddress() + "在" + sdf.format(new Date()) + "离开了");}//channel处于活动状态,提示:上线了@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {Channel channel = ctx.channel();log.info("{} 上线了" ,channel.remoteAddress());}//channel 处于不活动状态,提示离线了@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {Channel channel = ctx.channel();log.info("{} 离线了",channel.remoteAddress());}//转发消息@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {Channel ch1 = ctx.channel();System.out.println("ch1 = " + ch1);channels.forEach(ch ->{System.out.println("ch = " +  ch);if(ch1 != ch){ch.writeAndFlush("[用户]" + ch1.remoteAddress() + "在" +sdf.format(new Date()) + "时间发送了消息" + msg + "\n");}});}//发生异常时,关闭通道@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {ctx.close();}
}

3.4 客户端代码

NettyChatClient.java

@Slf4j
public class NettyChatClient {private  String host;private  int port;public NettyChatClient(String host, int port) {this.host = host;this.port = port;}private void run(){NioEventLoopGroup loopGroup = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap();bootstrap.group(loopGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast("decoder",new StringDecoder());pipeline.addLast("encoder",new StringEncoder());pipeline.addLast(new NettyChatClientHandler());}});ChannelFuture channelFuture = bootstrap.connect(host, port).sync();Channel channel = channelFuture.channel();log.info("客户端连接成功,地址是:{}",channel.remoteAddress());Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()){String msg = scanner.nextLine();channel.writeAndFlush(msg + "\r\n");}}catch (Exception e){e.printStackTrace();}finally {loopGroup.shutdownGracefully();}}public static void main(String[] args) {new NettyChatClient("127.0.0.1",8000).run();}
}

3.5 客户端Handler

NettyChatClientHandler.java

public class NettyChatClientHandler extends SimpleChannelInboundHandler<String> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {System.out.println(msg.trim());}
}

3.6 运行结果

服务端:

客户端001

 

客户端002

客户端003

 

这篇关于Netty学习——实战篇4 Netty开发Http服务实战、ByteBuf使用、开发群聊系统 备份的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

Linux系统中查询JDK安装目录的几种常用方法

《Linux系统中查询JDK安装目录的几种常用方法》:本文主要介绍Linux系统中查询JDK安装目录的几种常用方法,方法分别是通过update-alternatives、Java命令、环境变量及目... 目录方法 1:通过update-alternatives查询(推荐)方法 2:检查所有已安装的 JDK方

Java Thread中join方法使用举例详解

《JavaThread中join方法使用举例详解》JavaThread中join()方法主要是让调用改方法的thread完成run方法里面的东西后,在执行join()方法后面的代码,这篇文章主要介绍... 目录前言1.join()方法的定义和作用2.join()方法的三个重载版本3.join()方法的工作原

Spring AI使用tool Calling和MCP的示例详解

《SpringAI使用toolCalling和MCP的示例详解》SpringAI1.0.0.M6引入ToolCalling与MCP协议,提升AI与工具交互的扩展性与标准化,支持信息检索、行动执行等... 目录深入探索 Spring AI聊天接口示例Function CallingMCPSTDIOSSE结束语

Linux系统之lvcreate命令使用解读

《Linux系统之lvcreate命令使用解读》lvcreate是LVM中创建逻辑卷的核心命令,支持线性、条带化、RAID、镜像、快照、瘦池和缓存池等多种类型,实现灵活存储资源管理,需注意空间分配、R... 目录lvcreate命令详解一、命令概述二、语法格式三、核心功能四、选项详解五、使用示例1. 创建逻

在Java中使用OpenCV实践

《在Java中使用OpenCV实践》用户分享了在Java项目中集成OpenCV4.10.0的实践经验,涵盖库简介、Windows安装、依赖配置及灰度图测试,强调其在图像处理领域的多功能性,并计划后续探... 目录前言一 、OpenCV1.简介2.下载与安装3.目录说明二、在Java项目中使用三 、测试1.测

PyTorch中的词嵌入层(nn.Embedding)详解与实战应用示例

《PyTorch中的词嵌入层(nn.Embedding)详解与实战应用示例》词嵌入解决NLP维度灾难,捕捉语义关系,PyTorch的nn.Embedding模块提供灵活实现,支持参数配置、预训练及变长... 目录一、词嵌入(Word Embedding)简介为什么需要词嵌入?二、PyTorch中的nn.Em

C++中detach的作用、使用场景及注意事项

《C++中detach的作用、使用场景及注意事项》关于C++中的detach,它主要涉及多线程编程中的线程管理,理解detach的作用、使用场景以及注意事项,对于写出高效、安全的多线程程序至关重要,下... 目录一、什么是join()?它的作用是什么?类比一下:二、join()的作用总结三、join()怎么

mybatis中resultMap的association及collectio的使用详解

《mybatis中resultMap的association及collectio的使用详解》MyBatis的resultMap定义数据库结果到Java对象的映射规则,包含id、type等属性,子元素需... 目录1.reusltmap的说明2.association的使用3.collection的使用4.总

Spring Boot配置和使用两个数据源的实现步骤

《SpringBoot配置和使用两个数据源的实现步骤》本文详解SpringBoot配置双数据源方法,包含配置文件设置、Bean创建、事务管理器配置及@Qualifier注解使用,强调主数据源标记、代... 目录Spring Boot配置和使用两个数据源技术背景实现步骤1. 配置数据源信息2. 创建数据源Be

Java中使用 @Builder 注解的简单示例

《Java中使用@Builder注解的简单示例》@Builder简化构建但存在复杂性,需配合其他注解,导致可变性、抽象类型处理难题,链式编程非最佳实践,适合长期对象,避免与@Data混用,改用@G... 目录一、案例二、不足之处大多数同学使用 @Builder 无非就是为了链式编程,然而 @Builder