一、环境准备
开发工具eclipse/idea(本人目前正在切换为idea),maven插件
最新版的netty包maven依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha1</version>
</dependency>
二、简单的client/server demo
功能:client端发送一个“query time order”字符串,server端接收到后返回当前服务器时间
Server端的代码
public class TimeServer {
public void bind(int port) throws Exception{
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
try {
b.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChildChannelHandler());
//绑定端口,同步等待成功
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeServerHandler());
System.out.println("server add time server handler");
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
new TimeServer().bind(port);
}
}
TimeServer 首先创建了2个NioEventLoopGroup实例(NioEventLoopGroup是个线程组,它包含了一组NIO线程,专门用于网络事件的处理。),一个用于服务端接收客户端的链接,一个用于进行SokectChannel的网络读写。
ServerBootstrap是Netty用于启动NIO服务端的辅助启动类,group方法将两个线程组加入到进来。接着设置创建的Channel为NioServerSocketChannel。
然后配置NioServerSocketChannel的TCP参数,最后绑定I/O事件的处理器ChildChannelHandler。
f.channel().closeFuture().sync(); 方法进行阻塞,等待服务器链路关闭之后main函数才退出。
public class TimeServerHandler extends ChannelHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf)msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req,"UTF-8");
System.out.println("time server receive order is :"+body);
String res = "query time order".equals(body)?new Date().toString():"bad order";
ByteBuf resBuf = Unpooled.copiedBuffer(res.getBytes());
ctx.writeAndFlush(resBuf);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("server channel read complete……");
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("server has exception :"+cause.getMessage());
ctx.close();
}
}
TimeServerHandler 它用于对网络事件进行读写操作,通常我们只需要关注channelRead和exceptionCaught这两个方法
Client端代码
public class TimeClient {
public void connect(int port,String host) throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
try {
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeClientHandler());
System.out.println("client add time client handler");
}
});
//发起异步连接操作
ChannelFuture f = b.connect(host,port).sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
new TimeClient().connect(port,"127.0.0.1");
}
}
首先创建客户端处理I/O读写的NioEventLoopGroup线程组,然后继续创建客户端辅助启动类Bootstrap,然后对其进行配置。它的Channel需要设置为NioSocketChannel,然后添加handler。
public class TimeClientHandler extends ChannelHandlerAdapter{
private final ByteBuf firstMessage;
public TimeClientHandler() {
byte[] res = "query time order".getBytes();
this.firstMessage = Unpooled.buffer(res.length);
firstMessage.writeBytes(res);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client channel active");
ctx.writeAndFlush(firstMessage);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf)msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
System.out.println("Server time is : "+new String(req,"UTF-8"));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("client have exception : " + cause.getMessage());
ctx.close();
}
}
这里重点关注channelActive,channelRead和exceptionCaught这三个方法。
当客户端和服务端TCP链路建立成功后,Netty的线程会调用channelActive方法,发送查询时间指令给服务端,调用ChannelHandlerContext的writeAndFlush方法将请求发送给服务端。服务端返回消息应答时,channelRead被调用。
运行效果
Server端输出
server add time server handler
time server receive order is :query time order
server channel read complete……
Client端输出
client add time client handler
client channel active
Server time is : Wed Dec 09 16:28:53 CST 2015
参考《Netty权威指南(第二版)》