(一) 说明:
这是一个使用netty框架的demo,客户端发送一个消息到服务端,服务端将消息原样发送到客户端。
(二) 服务端:
(1) netty服务器端的ChannelHandler业务核心处理逻辑
服务器端的处理器Handler命名为HelloWorldChannelHandler,继承自ChannelInboundHandlerAdapter适配器,有如下重载方法:
我们只实现它的读取方法channelRead和异常处理方法exceptionCaught。
/**
* 业务核心处理逻辑
* @author comeCY
*
*/
public class HelloWorldServerHandler extends ChannelInboundHandlerAdapter{
/**
* 读取的方法
* 覆盖channelRead方法处理所有接收到的数据
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server channelRead..");
//服务端读取
System.out.println(ctx.channel().remoteAddress()+"->Server :"+ msg.toString());
//服务端转发
ctx.write("server write :"+msg);
ctx.flush(); //冲刷
}
/**
* 异常处理
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace(); //打印异常堆栈跟踪
ctx.close(); //关闭通道
}
}
(2) 引导服务器
- 创建 ServerBootstrap 实例来引导服务器并随后绑定
- 创建并分配一个 NioEventLoopGroup 实例来处理事件的处理,如接受新的连接和读/写数据。
- 指定本地 InetSocketAddress 给服务器绑定
- 通过 EchoServerHandler 实例给每一个新的 Channel 初始化
- 最后调用 ServerBootstrap.bind() 绑定服务器
public class HelloWorldServer {
private int port;
public HelloWorldServer(int port) {
this.port = port;
}
public void start() throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup(1); //创建EventLoopGroup
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap sbs = new ServerBootstrap()
.group(bossGroup,workerGroup) //创建ServerBootstrap
.channel(NioServerSocketChannel.class) //指定使用 NIO 的传输 Channel
.localAddress(new InetSocketAddress(port)) //设置 socket 地址使用所选的端口
.childHandler(new ChannelInitializer<SocketChannel>() {
//添加 到 Channel 的 ChannelPipeline
protected void initChannel(SocketChannel ch) throws Exception {
//添加字符串编码和解码器
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast("encoder", new StringEncoder());
//添加 HelloWorldSrverHandler
ch.pipeline().addLast(new HelloWorldServerHandler());
};
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true); //启用心跳保活机制
// 绑定端口,开始接收进来的连接
ChannelFuture future = sbs.bind(port).sync();
System.out.println("Server start listen at " + port );
future.channel().closeFuture().sync(); //关闭 channel 和 块,直到它被关闭
} finally {
bossGroup.shutdownGracefully(); //关闭 EventLoopGroup,释放所有资源
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new HelloWorldServer(port).start(); //呼叫服务器的start方法
}
}
(三) 客户端:
(1) 客户端Handler
HelloWorldClientHandler也是继承自ChannelInboundHandlerAdapter,实现方法channelActive,channelRead,exceptionCaught与服务端类似。
public class HelloWorldClientHandler extends ChannelInboundHandlerAdapter{
@Override
public void channelActive(ChannelHandlerContext ctx) {
//当被通知该 channel 是活动的时候打印
System.out.println("HelloWorldClientHandler Active");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//记录接收到的消息
System.out.println("HelloWorldClientHandler read Message:"+msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace(); //记录日志错误并关闭 channel
ctx.close();
}
}
(2) 引导客户端
- Bootstrap 被创建来初始化客户端
- NioEventLoopGroup 实例被分配给处理该事件的处理,这包括创建新的连接和处理 入站和出站数据
- InetSocketAddress 为连接到服务器而创建
- HelloWorldClientHandler 将被安装在 pipeline 当连接完成时
- Bootstrap.connect()被调用连接到远程的服务器
public class HelloWorldClient {
static final String HOST = System.getProperty("host", "127.0.0.1");
static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));
public static void main(String[] args) throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap(); //创建 Bootstrap
/* 指定 EventLoopGroup 来处理客户端事件。由于我们使用 NIO 传输,所以用到了
NioEventLoopGroup 的实现*/
b.group(group)
.channel(NioSocketChannel.class) //使用的 channel 类型是一个用于 NIO 传输
.option(ChannelOption.TCP_NODELAY, true) //关闭Nagle算法,实时发送数据
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
ChannelPipeline p = ch.pipeline();
p.addLast("decoder", new StringDecoder());
p.addLast("encoder", new StringEncoder());
p.addLast(new HelloWorldClientHandler());
}
});
ChannelFuture future = b.connect(HOST, PORT).sync(); //连接到远程;等待连接完成
//客户端写入
future.channel().writeAndFlush("Hello Netty Server, I am a common client...");
future.channel().closeFuture().sync(); //阻塞直到 Channel 关闭
} finally {
group.shutdownGracefully(); //调用 shutdownGracefully() 来关闭线程池和释放所有资源
}
}
}
(四) 运行效果:
(1) 服务端
(2) 客户端
(五) 结语
学习博客:
- http://blog.youkuaiyun.com/z69183787/article/details/52980361
- http://blog.youkuaiyun.com/z69183787/article/details/61623352
- http://blog.youkuaiyun.com/linuu/article/details/51306480
参考书籍:
- netty in action