Netty学习笔记

Netty的核心组件:    channel   回调   Future  事件和ChannelHandler

1 Channel  是java NIO的一个基本构造:(它代表一个实体(如:一个硬件设备,一个文件,一个网络套接字一个能够执行一个或者多个不同的IO操作的程序组件))

可以看做是传入(入站)或者传出(出站)数据的载体。它可以被打开或者关闭,连接或者断开连接

2 回调:一个方法,一个指向已经被提供给另外一个方法的方法的引用。使得后者可以在适当的时候调用前者。

3 Future :提供了一种在操作完成时通知应用程序的方式。可以看做是一个异步操作的结果的占位符。它在将来的某个时刻完成,并提供对其结果的访问

JDK java.util.concurrent.Future interface只允许手动检查对应的操作是否已经完成。或者一直阻塞,直到它完成。

Netty提供了自己的实现:ChannelFuture 用于在执行异步操作的时候使用。它提供了几种额外的方法。使得我们能够注册一个或者多个ChanneFutureListener实例

    监听器的回调方法operationComplete()将会在对应的操作完成时被调用。监听器可以判断操作是成功还是出错了。出错我们就可以检索产生的Throwable。

由ChannelFutureListener提供的通知机制消除了手动检查对应的操作是否完成的必要

每个Netty的出站I/O操作都将返回一个ChannelFuture。  Channel channel = ........;

ChannelFuture future  =  channel.connect(new InetSocketAddress("ipAddresss",xxx)); //异步连接到远程节点

也就是说,它们都不会阻塞。所以:Netty完全是异步和事件驱动的

如何利用 ChannelFutureListener

public class TestFuture {
public static void main(String[] args) {
Channel channel = null;
ChannelFuture future = channel.connect(new InetSocketAddress("ip",25));
future.addListener(new ChannelFutureListener() {

@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
ByteBuf buffer = Unpooled.copiedBuffer("Hello",Charset.defaultCharset());
ChannelFuture wf = (ChannelFuture) future.channel();
} else {
Throwable cause = future.cause();
cause.printStackTrace();
}
}
});
}
}

事件和ChannelHandler:使用不同的事件来通知我们状态的改变或者是操作的状态 ,可以认为每个ChannelHandler的实例都类似于一种为了响应特定事件而被执行的回调!

eg:记录日志,数据转换,流控制,应用程序逻辑

· channelHandler该组件实现了服务器对从客户端接收的数据的处理,即它的业务逻辑,它有助于保持业务逻辑与网络处理代码的分离。简化开发过程,因为代码必须不断地 演化以响应不断变化的需求


Echo服务器:至少一个ChannelHandler 

引导(配置服务器的启动代码):1绑定到服务器将在其上监听并接受传入连接请求的端口2:配置Channel,以将有关的入站消息

代码:

public class EchoServerHandler extends ChannelInboundHandlerAdapter{//可以实现ChannelInboundHandler接口


@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)throws Exception {
ByteBuf in = (ByteBuf) msg;
System.out.println("Server received: "+in.toString(CharsetUtil.UTF_8));
ctx.write(in);//将消息写给发送者,而不冲刷出站消息
}


@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();//打印异常栈跟踪
ctx.close();//关闭Channel
}

}

----------------------------------------------------------------------------------------------------------

@Sharable
public class EchoServer {
private final int port;


/**
* @param port
*/
public EchoServer(int port) {
this.port = port;
}
public static void main(String[] args) throws Exception{
if (args.length != 1) {
System.out.println("Usage: "+EchoServer.class.getSimpleName()+"<port>");
}
int port = 8080;//port的格式不正确,会出现NumberFormatException
new EchoServer(port).start();
}
public void start() throws Exception {
final EchoServerHandler serverHandler = new EchoServerHandler();
EventLoopGroup group = new NioEventLoopGroup();//1创建EventLoopGroup、若使用OIO传输:OioEventLoopGroup
try {
ServerBootstrap b = new ServerBootstrap();//2创建ServerBootStrap实例,指定NioEventLoopGroup来接受和处理新的连接
b.group(group).channel(NioServerSocketChannel.class)//3指定所使用的Nio传输Channel的类型,若使用Oio,OioServerSocketChannel
.localAddress(new InetSocketAddress(port))//4使用指定的端口设置套接字地址。将本地地址设置为具有端口号的InetSocketAddress。服务器将绑定这个地址以监听新的连接请求
//5ChannelInitializer的关键:当一个新的连接被接受时,一个新的子Channel将会被创建,添加一个EchoServerHandler到子Channel的Channelpipeline,。这个ChannelHandler将会接受到有关入站消息的通知
.childHandler(new ChannelInitializer<SocketChannel>() {


@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(serverHandler);//EchoServerHandler被标注为@Shareabel,所以我们可以总是使用同样的实例
}
});
ChannelFuture f = b.bind().sync();//6异步的绑定服务器;调用sync()方法阻塞等待直到绑定完成。
f.channel().closeFuture().sync();//7获取Channel的CloseFuture,并且阻塞当前线程直到它完成。
} finally {
group.shutdownGracefully().sync();//8关闭EventLoopGroup,释放所有的资源。
}
}
}
/**
 * EchoServerHandler实现了业务逻辑
 * main()方法引导了服务器
 * 引导过程中所需要的步骤如下:
 * 创建一个ServerBootStrap的实例,以引导和绑定服务器
 * 创建并分配一个NioEventLoopGroup实例以进行事件的处理:接受新连接以及读写数据;
 * 指定服务器绑定的本地的InetSocketAddress;
 * 使用一个EchoServerHandler的实例初始化每一个新的Channel;
 * 调用ServerBootStrap.bind()方法绑定服务器。
 * 服务器已经初始化,并且已经就绪能被使用了。
 */

---------------------------------------------------------------------------------------------

@Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
//channelRead方法处理完传入的消息,SimpleChannelInboundHandler负责释放指向保存该消息的ByteBuf的内存引用
@Override
//一个连接建立时被调用,确保数据将尽可能快的写入服务器
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!",CharsetUtil.UTF_8));
}

// 此方法已经换成messageReceived
// public void channelRead(ChannelHandlerContext ctx, ByteBuf in)throws Exception {
// System.out.println("Client received: "+in.toString(CharsetUtil.UTF_8));
// }




@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)throws Exception {
cause.printStackTrace();
ctx.close();
}




@Override
//每当接受数据时,都会调用这个方法。,eg:服务器发送5个字节:第一次使用持有3个字节的byteBuf(Netty的字节容器
//第二次使用一个持有2字节的ByteBuf。作为一个面向流的协议。Tcp保证了字节数组将会按照服务器发送它们的顺序被接收
protected void messageReceived(ChannelHandlerContext ctx, ByteBuf in)
throws Exception {
System.out.println("Client received: "+in.toString(CharsetUtil.UTF_8));

}

}

-----------------------------------------------------------------

public class EchoClient {
private final String host;
private final int port;
/**
* @param host
* @param port
*/
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws Exception{
//为进行事件处理分配一个NIOEventGroup实例。其中,事件处理包括:创建新的连接,处理入站和出站的数据;
EventLoopGroup group = new NioEventLoopGroup();//可以使用OIO传输服务端和客户端可以选择不同的传输
try {
Bootstrap b = new Bootstrap();//创建BootStrap
b.group(group)//指定EventLoopGroup以处理客户端事件;需要适用于NIO的实现
.channel(NioSocketChannel.class)//适用于NIO传输的Channel类型
.remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {//连接被建立时,创建Channel时,向ChannelPipeline中添加一个EchoClientHandler实例


@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoClientHandler());//向ChannelPipeline中添加一个客户端实例
}
});
ChannelFuture f = b.connect().sync();//一切都设置完成后,调用BootStrap的connect()方法,连接到远程节点,阻塞等待,直到连接完成。
f.channel().closeFuture().sync();//阻塞。直到Channel关闭
} finally {
group.shutdownGracefully().sync();//关闭线程池,并且释放所有的资源。
}
}
public static void main(String[] args) throws Exception {
if (args.length !=2) {
System.out.println("Usage: "+EchoClient.class.getSimpleName()+"<host><port>");
return;
}
String host = args[0];
int port = Integer.parseInt(args[1]);
new EchoClient(host, port).start();
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值