初识Netty(使用Netty实现服务端与客户端)
初识Netty(使用Netty实现服务端与客户端)
一、Netty简介
Netty 是基于 Java NIO 的异步事件驱动的网络应用框架,使用 Netty 可以快速开发网络应用,Netty 提供了高层次的抽象来简化 TCP 和 UDP 服务器的编程,但是你仍然可以使用底层的 API。
Netty 的内部实现是很复杂的,但是 Netty 提供了简单易用的API从网络处理代码中解耦业务逻辑。Netty 是完全基于 NIO 实现的,所以整个 Netty 都是异步的。
Netty 是最流行的 NIO 框架,它已经得到成百上千的商业、商用项目验证,许多框架和开源组件的底层 rpc 都是使用的 Netty,如 Dubbo、Elasticsearch 等等。下面是官网给出的一些 Netty 的特性:
设计方面
- 对各种传输协议提供统一的 API(使用阻塞和非阻塞套接字时候使用的是同一个 API,只是需要设置的参数不一样)。
- 基于一个灵活、可扩展的事件模型来实现关注点清晰分离。
- 高度可定制的线程模型——单线程、一个或多个线程池。
- 真正的无数据报套接字(UDP)的支持(since 3.1)。
易用性
- 完善的 Javadoc 文档和示例代码。
- 不需要额外的依赖,JDK 5 (Netty 3.x) 或者 JDK 6 (Netty 4.x) 已经足够。
性能
- 更好的吞吐量,更低的等待延迟。
- 更少的资源消耗。
- 最小化不必要的内存拷贝。
安全性
- 完整的 SSL/TLS 和 StartTLS 支持
二、一个简单的网络服务器
我这里使用的开发环境是idea+maven+netty4,pom中的maven依赖的版本是
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.33.Final</version>
</dependency>
服务端代码
public class NettyMsgServer {
public static void getMsg() throws InterruptedException {
ServerBootstrap bootstrap = new ServerBootstrap();
EventLoopGroup mainEventLoopGroup = new NioEventLoopGroup(1);
EventLoopGroup subEventLoopGroup = new NioEventLoopGroup(2);
bootstrap.group(mainEventLoopGroup, subEventLoopGroup);
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.localAddress(17777);
bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline().addLast(new NettyMsgServerHandler());
}
});
ChannelFuture channelFuture = bootstrap.bind().sync();
System.out.println("Netty服务器启动成功,监听链接....");
ChannelFuture closeFuture = channelFuture.channel().closeFuture();
closeFuture.sync();
subEventLoopGroup.shutdownGracefully();
mainEventLoopGroup.shutdownGracefully();
}
public static void main(String[] args) throws InterruptedException {
getMsg();
}
}
服务端消息处理Handler
// 我们是服务端,服务端是不是 进行消息的 接收啊,所以是in
public class NettyMsgServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//对接受到的消息进行处理
ByteBuf in = (ByteBuf) msg;
try {
while (in.isReadable()) {
System.out.print((char) in.readByte());
}
System.out.println();
} finally {
ReferenceCountUtil.release(msg);
}
}
}
三、一个简单的用户端
用户端代码
public class NettyMsgCilent {
public static void sendMsg() throws InterruptedException {
Bootstrap bootstrap = new Bootstrap();
//客户端,也是要接收服务端的返回值的,既然客户端需要接收服务端的消息,那么客户端也需要
//知道io何时达到,也需要有selector进行io状态的轮询,因为一个 client可能链接好几个server
//但是客户端只需要一个group就行了,不需要main的group,因为客户端的链接状态是 服务端进行创建的
EventLoopGroup eventLoopGroup = new NioEventLoopGroup(2);
bootstrap.group(eventLoopGroup);
//设置channel类型
bootstrap.channel(NioSocketChannel.class);
//绑定ip、 port
bootstrap.remoteAddress("127.0.0.1", 17777);
//参数设置
bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
//pipeline和handler设置
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new NettyMsgCilentHandler());
}
});
ChannelFuture channelFuture = bootstrap.connect();
channelFuture.addListener((ChannelFuture listener) -> {
if(listener.isSuccess()) {
System.out.println("连接 成功!");
} else {
System.out.println("连接 失败! 可以进行后续的补救措施!");
}
});
channelFuture.sync(); // 我们的网络编程,比如 聊天工具,客户端的 连接阻塞只影响 你
Channel channel = channelFuture.channel();
Scanner scanner = new Scanner(System.in);
System.out.println("请输入发送内容:");
while(scanner.hasNext()) {
String next = scanner.next();
Date date = new Date();
String pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
String time = sdf.format(date);
byte[] bytes = (time + ">>" + next).getBytes();
ByteBuf buffer = channel.alloc().buffer();
buffer.writeBytes(bytes);
channel.writeAndFlush(buffer);
System.out.println("消息发送完成!");
}
eventLoopGroup.shutdownGracefully();
}
public static void main(String[] args) throws InterruptedException {
sendMsg();
}
}
用户端消息处理handler
public class NettyMsgCilentHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("read!");
}
}