标签:
本章介绍
本章中将编写一个基于netty的server和client来互相通信,我们首先来设置netty的开发环境。
从上图中能够看出,server会写数据到client而且处理多个client的并发连接。从理论上来说,限制程序性能的因素仅仅有系统资源和JVM。为了方便理解,这里举了个生活样例。在山谷或高山上大声喊,你会听见回声。回声是山返回的;在这个样例中,你是client。山是server。喊的行为就类似于一个Nettyclient将数据发送到server,听到回声就类似于server将同样的数据返回给你,你离开山谷就断开了连接。可是你能够返回进行重连server而且能够发送很多其它的数据。
写一个Nettyserver主要由两部分组成:
通过创建ServerBootstrap对象来启动server。然后配置这个对象的相关选项。如port、线程模式、事件循环,而且加入逻辑处理程序用来处理业务逻辑(以下是个简单的应答server样例)
package netty.example;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public void start() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
//create ServerBootstrap instance
ServerBootstrap b = new ServerBootstrap();
//Specifies NIO transport, local socket address
//Adds handler to channel pipeline
b.group(group).channel(NioServerSocketChannel.class).localAddress(port)
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new EchoServerHandler());
}
});
//Binds server, waits for server to close, and releases resources
ChannelFuture f = b.bind().sync();
System.out.println(EchoServer.class.getName() + "started and listen on ?" + f.channel().localAddress());
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
new EchoServer(65535).start();
}
}
从上面这个简单的server样例能够看出,启动server应先创建一个ServerBootstrap对象。由于使用NIO,所以指定NioEventLoopGroup来接受和处理新连接。指定通道类型为NioServerSocketChannel,设置InetSocketAddress让server监听某个端口已等待client连接。
最后绑定server等待直到绑定完毕。调用sync()方法会堵塞直到server完毕绑定,然后server等待通道关闭。由于使用sync(),所以关闭操作也会被堵塞。如今你能够关闭EventLoopGroup和释放全部资源,包含创建的线程。
这个样例中使用NIO,由于它是眼下最经常使用的传输方式。你可能会使用NIO非常长时间。可是你能够选择不同的传输实现。比如。这个样例使用OIO方式传输,你须要指定OioServerSocketChannel。Netty框架中实现了多重传输方式,将再后面讲述。
本小节重点内容:
你的channel handler必须继承ChannelInboundHandlerAdapter而且重写channelRead方法,这种方法在不论什么时候都会被调用来接收数据,在这个样例中接收的是字节。
以下是handler的实现,事实上现的功能是将client发给server的数据返回给client:
package netty.example;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("Server received: " + msg);
ctx.write(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
Netty使用多个Channel Handler来达到对事件处理的分离,由于能够非常容的加入、更新、删除业务逻辑处理handler。Handler非常easy,它的每一个方法都能够被重写,它的全部的方法中仅仅有channelRead方法是必需要重写的。
package netty.example;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.example.echo.EchoClientHandler;
import java.net.InetSocketAddress;
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture f = b.connect().sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
new EchoClient("localhost", 20000).start();
}
}
创建启动一个client包括以下几步:和编写server的ChannelHandler一样,在这里将自己定义一个继承SimpleChannelInboundHandler的ChannelHandler来处理业务;通过重写父类的三个方法来处理感兴趣的事件:
package netty.example;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.write(Unpooled.copiedBuffer("Netty rocks!",CharsetUtil.UTF_8));
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
System.out.println("Client received: " + ByteBufUtil.hexDump(msg.readBytes(msg.readableBytes())));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
可能你会问为什么在这里使用的是SimpleChannelInboundHandler而不使用ChannelInboundHandlerAdapter?主要原因是ChannelInboundHandlerAdapter在处理完消息后须要负责释放资源。在这里将调用ByteBuf.release()来释放资源。SimpleChannelInboundHandler会在完毕channelRead0后释放消息,这是通过Netty处理全部消息的ChannelHandler实现了ReferenceCounted接口达到的。
注意,netty4须要jdk1.7+。
本人測试,能够正通常执行。
本章介绍如何编写基于一个简单的Netty的server和client和发送数据通信。它描述了如何创建server和client以及Netty异常处理机制。
Netty In Action中国版 - 第二章:第一Netty程序
标签:
原文地址:http://www.cnblogs.com/yxwkf/p/5041432.html