〇. 准备工作
- 导包
1 | xml复制代码<dependency> |
一. Server端
- server从宏观上来看,会处理两方面的事件:客户端连接请求、业务相关的事件(包括server端接受client端的消息、client端断开连接等等)。
- 针对这两类事件,netty分别采用了两个线程组来实现。当有连接请求过来的时候,使用BossGroup进行处理;当连接成功之后,将该channel传递给WorkerGroup,WorkerGroup管理该channel的请求。
1 | java复制代码EventLoopGroup bossGroup = new NioEventLoopGroup(1); |
其中,值得说明的是,NioEventLoopGroup构造方法传递的参数,表示该线程组里面的线程池个数。上述表示,bossGroup有一个线程池负责处理连接请求。如果不提供参数,就默认为CPU核心数*2。
- 之后就可以初始化netty server的启动类
ServerBootstrap
了 - 首先,将上面初始化的两个
EventLoopGroup
作为参数放入。 - 设置通道类型为
NioServerSocketChannel
- 设置已连接队列的大小,这个参数与TCP建立连接有关。详细可看:blog.csdn.net/weixin_4473…
- 将连接设置为keep-alive,避免短时间内重复多次TCP握手。详细可看:www.cnblogs.com/caoweixiong…
- 设置
childHandler
(实际上处理事件的是处理器链,后面再详细描述)。childHandler
就是处理WorkerGroup
的事件。在这首先添加一个初始化器,在初始化器里面给处理链添加了一个自定义的真正处理类的类MessageHandler
。
1 | java复制代码ServerBootstrap bootstrap = new ServerBootstrap() |
- 一般处理器都会实现
ChannelInboundHandlerAdapter
或SimpleChannelInboundHandler
。SimpleChannelInboundHandler
是ChannelInboundHandlerAdapter
的子类,会帮忙释放直接内存,避免内存泄露,因此继承SimpleChannelInboundHandler
会比较方便。 channelRead0
方法就是真正处理消息的方法- 指定client端发送ByteBuf类型的数据,因此接收的时候直接获取即可。(也可以指定不同类型的数据,需要指定不同的编解码器)
ChannelHandlerContext
是该通道处理器相关的上下文对象,携带了通道本身、pipline等上下文。详细内容之后再分析。在本次代码中,通过上下文获取到channel本身,再通过channel获取到client端的ip地址。- 最好重写
exceptionCaught
,及时捕获异常并且进行处理。
1 | java复制代码public class MessageHandler extends SimpleChannelInboundHandler<ByteBuf> { |
- 完成启动类的初始化之后,还没完成。因为server端没有正式启动。
bind
方法表示服务在PORT端口正式启动,并且调用sync方法,表示同步阻塞等待启动完成。
1 | java复制代码ChannelFuture channelFuture = bootstrap.bind(PORT).sync(); |
- 获取到
channelFuture
对象之后,就可以同步阻塞地监听关闭事件了。
1 | java复制代码channelFuture.channel().closeFuture().sync(); |
- 为了在关闭服务的时候,及时将线程池关闭,需要在finally时,关闭所有线程池
1 | java复制代码bossGroup.shutdownGracefully(); |
- 至此,最简单的一个netty server已完成。
- 完整代码如下
1 | java复制代码public class HelloWorldServer { |
二、Client端
- 对于client端来说,会比server端少一个处理连接请求的
EventLoopGroup
,只有一个处理基本业务的EventLoopGroup
。
1 | java复制代码EventLoopGroup eventExecutors = new NioEventLoopGroup(); |
- client的启动类为
Bootstrap
- 设置线程组为
eventExecutors
- 设置通道类型为
NioSocketChannel
(注意,与server端的是不一样的) - 因为只有线程组,因此handler也只有一组,因此在handler方法里面放一个初始化器就好。在初始化器里面再添加事件的真正处理类。
1 | java复制代码Bootstrap bootstrap = new Bootstrap() |
- 这个处理器重写了
channelActive
方法,表示当通道创建成功之后就运行的方法。该方法用于接收用户输入的消息,并通过通道发送给server。 - 其中,
writeAndFlush
方法可以实现写入缓存并发送给server。Unpooled.copiedBuffer
可以将Sring类型的字符串变成netty自定义的一个二进制缓存类ByteBuf
。
1 | java复制代码public class SendMessageHandler extends ChannelInboundHandlerAdapter { |
- 启动类通过连接指定的地址、端口号实现连接,并且调用
sync()
方法实现同步阻塞等待。 - 并且同步阻塞地等待关闭通道事件。
1 | java复制代码ChannelFuture channelFuture = bootstrap.connect(ADDRESS, PORT).sync(); |
- 最后,需要保证在关闭client端之后,优雅地关闭线程组
1 | java复制代码eventExecutors.shutdownGracefully(); |
- 至此,完成了最简单的netty client端实现。
- 以下是完整代码:
1 | java复制代码public class HelloWorldClient { |
本文转载自: 掘金