从官网入门文档中找到最简化的完整的服务端与服务端的代码。
一个 Netty 服务端的代码实在太精简了,只考虑好几个部分就可以:初始化、编/解码器、数据处理。非官方的笼统的说记住这三步骤就足够了。
初始化部分是信息量最多的部分,在这里需要创建线程模型,指定网络参数配置,将编/解码器,数据处理器等进行配置,然后绑定到一个端口就可以。
//NettyServer.java
public class NettyServer {
static final int PORT = 9000;
public static void main(String[] args) throws InterruptedException {
//监听线程(池)
EventLoopGroup bossGroup = new NioEventLoopGroup();
//工作线程(池)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//服务端的辅助类
ServerBootstrap b = new ServerBootstrap();
//将线程组配置进服务
b.group(bossGroup, workerGroup)
//指定服务端监听类
.channel(NioServerSocketChannel.class)
//配置网络参数
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY,true)
//配置一个日志处理器
.handler(new LoggingHandler(LogLevel.INFO))
//定义一个匿名初始始化器,用来指定挂载器
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
//后面定义处理器
p.addLast(new EchoServerHandler());
}
});
//启动监听线程,绑定在一个指定的端口上
ChannelFuture f = b.bind(PORT).sync();
// 关闭了网络监听后
f.channel().closeFuture().sync();
} finally {
// 完全关闭后
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
编/解码器按照接收和发送的顺序处理,这里以熟悉代码结构为主可以忽略,在一个实际项目中是很难避免不使用编/解码器的。
这里继承 ChannelInboundHandlerAdapter
类的是接收处理器,如果是继承 'ChannelOutboundHandlerAdapter
类的是发送类型的处理器。
//EchoServerHandler.java
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf=(ByteBuf)msg;
System.out.println("客户端发送消息是:"+buf.toString(CharsetUtil.UTF_8));
System.out.println("客户端地址:"+ctx.channel().remoteAddress());
ctx.write(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
望码知意,回调函数都是被框架自动调用的。
客户端大体结构与服务端很类似
EchoClient.java
public class EchoClient {
static final String HOST = System.getProperty("host", "127.0.0.1");
static final int PORT = Integer.parseInt(System.getProperty("port", "9000"));
static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));
public static void main(String[] args) throws Exception {
// 这里只配置工作线程即可
EventLoopGroup group = new NioEventLoopGroup();
try {
//客户端辅助类
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new EchoClientHandler());
}
});
// 连接
ChannelFuture f = b.connect(HOST, PORT).sync();
// 结束
f.channel().closeFuture().sync();
} finally {
// 回收线程
group.shutdownGracefully();
}
}
}
编/解码和服务端要相对应,是类似的,这里可以不处理。
客户端的收发也是在处理器
//EchoClientHandler.java
public class EchoClientHandler extends ChannelInboundHandlerAdapter {
private final ByteBuf firstMessage;
/**
* Creates a client-side handler.
*/
public EchoClientHandler() {
firstMessage = Unpooled.buffer(EchoClient.SIZE);
for (int i = 0; i < firstMessage.capacity(); i ++) {
firstMessage.writeByte((byte) i);
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(firstMessage);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.write(msg);
System.out.println(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}