Netty基础知识
Netty 是一个 NIO client-server(客户端服务器)框架, 使用 Netty
可以快速开发网络应用,例如服务器和客户端协议。Netty
提供了一种新的方式来使开发网络应用程序,这种新的方式使得它很容易使用和有很强的扩展性。Netty 的内部实现时很复杂的,但是 Netty
提供了简单易用的 api 从网络处理代码中解耦业务逻辑。Netty 是完全基于 NIO 实现的,所以整个 Netty 都是异步的。
网络应用程序通常需要有较高的可扩展性,无论是 Netty 还是其他的基于 Java NIO的框架,都会提供可扩展性的解决方案。Netty 中一个关键组成部分是它的异步特性。
为什么使用 Netty ?
- 作为一个 NIO client-server 框架,Netty 提供了这样的一个间接的解决方法。Netty 提供了高层次的抽象来简化
TCP 和 UDP 服务器的编程,但是你仍然可以使用底层地 API。 - 多年来,Netty 变的更广为人知,它是 Java 网络的首选框架,在一些开源或非开源的项目中可以体现。并且,Netty 在 2011
年获得 Duke’s Choice Award(Duke’s Choice 奖)。此外,在 2011 年,Netty 的创始人 Trustion Lee 离开 RedHat 后加入 Twitter,在这一点上,Netty 项目奖会成为一个独立的项目组织。RedHat 和 Twitter 都使用 Netty,所以它毫不奇怪。使用 Netty 的项目越来越多,Netty 的用户群体和项目以及 Netty 社区都是非常活跃的。 - NIO 是一个比较底层的 APIs,它依赖于操作系统的 IO APIs。Java 实现了统一的接口来操作
IO,其在所有操作系统中的工作行为是一样的,这是很伟大的。使用 NIO 会经常发现代码在 Linux 上正常运行,但在 Windows
上就会出现问题。我建议你如果使用 NIO 编写程序, 就应该在所有的操作系统上进行测试来支持,
使程序可以在任何操作系统上正常运行;即使在所有的 Linux 系统上都测试通过了,也要在其他的操作系统上进行测试;你若不
验证,以后就可能会出问题。 - NIO2 看起来很理想,但是 NIO2 只支持 Jdk1.7+,若你的程序在 Java1.6 上运行,则无法使用
NIO2。另外,Java7 的 NIO2 中没有提供 DatagramSocket 的支持,所以 NIO2只支持 TCP 程序,不支持
UDP 程序。 Netty 提供一个统一的接口, 同一语义无论在 Java6 还是 Java7 的环境下都是可以运行的,开发者无需关心底层
APIs 就可以轻松实现相关功能。
Netty官网下载
下载地址 http://netty.io/
服务端
package com.cyq.server;
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 {
//指定 NioEventLoopGroup 来接受和处理新连接
EventLoopGroup group = new NioEventLoopGroup();
try {
//create ServerBootstrap instance
ServerBootstrap b = new ServerBootstrap();
//Specifies NIO transport, local socket address
/*Adds handler to channel pipeline 指定通道类型为 NioServerSocketChannel
设置 InetSocketAddress 让服务器监听某个端口已等待客户端连接
*/
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
* 绑定服务器等待直到绑定完成,调用 sync()方法会阻塞直到服务器完成绑定,
* 然后服务器等待通道关闭,因为使用 sync(),所以关闭操作也会被阻塞。
* 现在你可以关闭EventLoopGroup 和释放所有资源,包括创建的线程。
* */
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();
}
}
package com.cyq.server;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
public class EchoServerHandler extends ChannelHandlerAdapter{
@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();
}
}
客户端
package com.cyq.client;
import java.net.InetSocketAddress;
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;
/**
* @author cyq
* 创建 Bootstrap 对象用来引导启动客户端
创建 EventLoopGroup 对象并设置到 Bootstrap 中,EventLoopGroup 可以理解为是一个线程池,这个线程池用来处理连接、接受数据、发送数据
创建 InetSocketAddress 并设置到 Bootstrap 中,InetSocketAddress 是指定连接的服务器地址
添加一个 ChannelHandler,客户端成功连接服务器后就会被执行
调用 Bootstrap.connect()来连接服务器
最后关闭 EventLoopGroup 来释放资源
*/
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", 65535).start();
}
}
package com.cyq.client;
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.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!",CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
@Override
protected void messageReceived(ChannelHandlerContext arg0, ByteBuf msg) throws Exception {
// TODO Auto-generated method stub
System.out.println("Client received: " + ByteBufUtil.hexDump(msg.readBytes(msg.readableBytes())));
}
}