原文博客地址: pjmike的博客java
这篇主要介绍一个Netty 客户端与服务端的示例代码,对Netty有一个直观感觉,看看如何使用Netty,后续文章会对Netty的各个组件进行详细分析git
Netty是一款异步的事件驱动的网络应用程序框架,支持快速开发可维护的高性能的面向协议的服务器和客户端。Netty主要是对java 的 nio包进行的封装github
上面介绍到 Netty是一款 高性能的网络通信框架,那么咱们为何要使用Netty,换句话说,Netty有哪些优势让咱们值得使用它,为何不使用原生的 Java Socket编程,或者使用 Java 1.4引入的 Java NIO。接下来分析分析 Java Socket编程和 Java NIO。编程
首先来看一个Java 网络编程的例子:安全
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
System.out.println("received: " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
复制代码
上面展现了一个简单的Socket服务端例子,该代码只能同时处理一个链接,要管理多个并发客户端,须要为每一个新的客户端Socket建立一个 新的Thread。这种并发方案对于中小数量的客户端来讲还能够接受,若是是针对高并发,超过100000的并发链接来讲该方案并不可取,它所须要的线程资源太多,并且任什么时候候均可能存在大量线程处于阻塞状态,等待输入或者输出数据就绪,整个方案性能太差。因此,高并发的场景,通常的Java 网络编程方案是不可取的。服务器
仍是先来看一个 Java NIO的例子:网络
public class ServerSocketChannelDemo {
private ServerSocketChannel serverSocketChannel;
private Selector selector;
public ServerSocketChannelDemo(int port) throws IOException {
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(port));
selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
public void listener() throws IOException {
while (true) {
int n = selector.select();
if (n == 0) {
continue;
}
Iterator iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = (SelectionKey) iterator.next();
if (selectionKey.isAcceptable()) {
ServerSocketChannel server_channel = (ServerSocketChannel) selectionKey.channel();
SocketChannel socketChannel = server_channel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
}
if (selectionKey.isReadable()) {
//若是通道处于读就绪的状态
//读操做
//TODO
}
}
}
}
}
复制代码
NIO的核心部分主要有:并发
选择器 Selector 是 Java 非阻塞 I/O实现的关键,将通道Channel注册在 Selector上,若是某个通道 Channel发送 读或写事件,这个Channel处于就绪状态,会被Selector轮询出来,进而进行后续I/O操做。这种I/O多路复用的方式相比上面的阻塞 I/O模型,提供了更好的资源管理:框架
尽管使用 Java NIO可让咱们使用较少的线程处理不少链接,可是在高负载下可靠和高效地处理和调度I/O操做是一项繁琐并且容易出错的任务,因此才引出了高性能网络编程专家——Netty异步
Netty有不少优秀的特性值得让咱们去使用它(摘自《Netty实战》):
设计
性能
健壮性
安全性:
易用:
下面是server 和client的示例代码,先来看看Netty代码是怎么样的,后续文章会详细分析各个模块。
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public static void main(String[] args) throws InterruptedException {
new EchoServer(8888).start();
}
public void start() throws InterruptedException {
final EchoServerHandler serverHandler = new EchoServerHandler();
//建立EventLoopGroup,处理事件
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(boss,worker)
//指定所使用的NIO传输 Channel
.channel(NioServerSocketChannel.class)
//使用指定的端口设置套接字地址
.localAddress(new InetSocketAddress(port))
//添加一个EchoServerHandler到子Channel的ChannelPipeline
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//EchoServerHandler标志为@Shareable,因此咱们能够老是使用一样的实例
socketChannel.pipeline().addLast(serverHandler);
}
});
//异步的绑定服务器,调用sync()方法阻塞等待直到绑定完成
ChannelFuture future = b.bind().sync();
future.channel().closeFuture().sync();
} finally {
//关闭EventLoopGroup,释放全部的资源
group.shutdownGracefully().sync();
worker.shutdownGracefully().sync();
}
}
}
复制代码
EchoServerHandler
@ChannelHandler.Sharable //标识一个 ChannelHandler能够被多个Channel安全地共享
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buffer = (ByteBuf) msg;
//将消息记录到控制台
System.out.println("Server received: " + buffer.toString(CharsetUtil.UTF_8));
//将接受到消息回写给发送者
ctx.write(buffer);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//将未消息冲刷到远程节点,而且关闭该 Channel
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//打印异常栈跟踪
cause.printStackTrace();
//关闭该Channel
ctx.close();
}
}
复制代码
代码要点解读:
ServerBootStrap
是引导类,帮助服务启动的辅助类,能够设置 Socket参数EventLoopGroup
是处理I/O操做的线程池,用来分配 服务于Channel的I/O和事件的 EventLoop
,而NioEventLoopGroup
是EventLoopGroup
的一个实现类。这里实例化了两个 NioEventLoopGroup
,一个 boss
,主要用于处理客户端链接,一个 worker
用于处理客户端的数据读写工做EchoServerHandler
实现了业务逻辑ServerBootStrap.bind()
方法以绑定服务器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 InterruptedException {
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 socketChannel) throws Exception {
socketChannel.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture channelFuture = b.connect().sync();
channelFuture.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws InterruptedException {
new EchoClient("127.0.0.1", 8888).start();
}
}
复制代码
EchoClientHandler
@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
System.out.println("Client received: "+byteBuf.toString());
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks",CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
复制代码
代码要点解读:
ServerBootStrap
同样,也是一个引导类,主要辅助客户端NioEventLoopGroup
实例,里面的 EventLoop
,处理链接的生命周期中所发生的事件EchoClientHandler
类负责处理业务逻辑,与服务端的EchoSeverHandler
做用类似。