版权声明:本博客所有的原创文章,转载请注明出处,作者皆保留版权。 https://blog.csdn.net/anLA_/article/details/83042562
介绍
Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,它简化了程序员的工作,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
本系列文章讲慢慢一起走进学习Netty
本篇文章就以一个Hello Word程序开始。
例子
首先,Netty作为网络编程的框架,自然离不开Socket,同时,也包括Server端以及Client端。他们利用Socket进行信息传输。
Socket可以理解为应用层与TCP/IP协议族通信的中间软件抽象层。
关于Socket可看文章:Socket
下面看demo
Client端
首先是一个启动的类:
public class Client {
private Integer port;
private String address;
public Client(Integer port, String address){
this.port = port;
this.address = address;
}
/**
* 用于连接服务器
*/
public void start() throws Exception{
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new HelloClientHandler());
}
});
System.out.println("begin to connect...");
ChannelFuture future = b.connect(this.address, this.port);
future.channel().closeFuture().sync();
}
public static void main(String[] args) throws Exception {
Client client = new Client(8989, "127.0.0.1");
client.start();
}
}
再看看里面的HelloClientHandler
public class HelloClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
System.out.println("Client received Message from server: " + msg.toString(CharsetUtil.UTF_8)); //读取数据就输出
}
@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(); //遇到错误就关闭
}
}
对于上面的client端的代码,可以看到下面几个概念:
EventLoopGroup
:可以理解为线程池,里面产生EventLoop
来执行channel
所有产生的事件Bootstrap
:启动器,讲Netty
各个组件串起来使之运行Pipline
:可以理解为管道,或者流水线,里面可以装载多个ChannelHandler并让他们执行ChannelHandler
:例如HelloClientHandler
,具体的channel
所处理的工作
整个过程可以理解为这样一个过程,分发快递过程,快递车把快递送到目的地,此时有一个或者多个快递员(EventLoop)进行快递分发,分发到给不同的区域标签(Pipline),此时,由于快递是已经定义好的,再有快递员发给不同的快递手上(执行ChannelHandler所定一个的规则)
EventLoop 的作用是一个死循环,而这个循环中做3件事情:
- 有条件的等待 NIO 事件
- 处理 NIO 事件
- 处理消息队列中的任务
下面看看服务端代码
Server端
首先也是一个启动类:
public class Server {
private Integer port;
public Server(Integer port){
this.port = port;
}
public void startServer() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(group)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
System.out.println("new channel");
ch.pipeline().addLast(new HelloServerHandler());
}
});
System.out.println("begin to run server");
ChannelFuture future = serverBootstrap.bind().sync();
future.channel().closeFuture().sync();
}finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
Server server = new Server(8989);
server.startServer();
}
}
再看HelloServerHandler,和前面的HelloClientHandler逻辑差不多:
public class HelloServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
System.out.println("Server received from client: " + in.toString(CharsetUtil.UTF_8));
ctx.write(in); //发送出去,所以client端可以读
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("server active");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println(cause);
ctx.close();
}
}
就看启动代码来看,服务端代码和客户端代码还是很相似的
Bootstrap
变为ServerBootstrap
NioSocketChannel
变为NioServerSocketChannel
- 而在Handler处理上,client端的
handler
变为了childHandler
整个例子的流程如下:
- 服务端启动,监听127.0.0.1:8989端口
- 客户端连接端口,服务端监听到,输出
new channel
并把HelloServerHandler
加入到pipline
中 - 此时,客户端连接上后,触发了
channelActive
事件,并把Netty rocks!
发了出去 - 此时,服务端接受到了数据
Netty rocks!
,触发了HelloServerHandler
中事件,所以打印了Server received: Netty rocks!
,并且发送了出去 - 此时,由于客户端接受消息,触发了
channelRead0
所以最后打印了Client received: Netty rocks!
整个例子就完成了。
资料:
- Netty In Action