netty基础03_传输
1.传输方式
netty中的主要传输方式:
1).OIO
也就是阻塞式io;
1】netty实现oio服务端
只是接受客户端连接并发送“hello world”字符串消息到客户端,发送完了就断开连接
public class NettyOioServer { //netty阻塞io服务 public void server(int port)throws Exception{ final ByteBuf buf = Unpooled.unreleasableBuffer( Unpooled.copiedBuffer("hello world\r\n", Charset.forName("UTF-8")) ); EventLoopGroup group = new OioEventLoopGroup(); //这里创建的是OioEventLoopGroup对象,对应阻塞io try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(group) //因为是阻塞io,启动类中只需要注册一个EventLoopGroup; .channel(OioServerSocketChannel.class) .localAddress(new InetSocketAddress(port)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { //用来处理请求,作用是当客户端连接成功时 ctx.writeAndFlush(buf.duplicate()).addListener(ChannelFutureListener.CLOSE); //添加监听器,作用是当发送完消息后就断开连接; } }); } }); ChannelFuture f = bootstrap.bind().sync(); f.channel().closeFuture().sync(); } finally { group.shutdownGracefully().sync(); } } //主函数 public static void main(String[] args) throws Exception{ NettyOioServer server = new NettyOioServer(); server.server(8000); } }
2】测试
因为服务端与客户端通常用socket通信;
socket通信不在乎服务端的用什么语言编写;
因此可以利用telnet来作为客户端发送请求;
widows中telnet功能默认关闭;
开启telnet:控制面板 -》程序 -》程序和功能 -》打开或关闭widows功能 -》勾选Telnet客户端
使用telnet发请求:
打开命令行窗口(cmd),开启telnet:
telnet
连接服务端:
open 127.0.0.1 8000
收到了服务端的请求:返回hello world并断开连接;
2)NIO
因为netty做了封装,当换用nio实现相同功能的服务器时,只需要修改少量的代码即可;
主要是:
EventLoopGroup换成NioEventLoopGroup;
NIO需要两个EventLoopGroup,一个用来连接客户端,另一个用来处理请求;
public class NettyNioServer { //netty阻塞io服务 public void server(int port)throws Exception{ final ByteBuf buf = Unpooled.unreleasableBuffer( Unpooled.copiedBuffer("hello world\r\n", Charset.forName("UTF-8")) ); EventLoopGroup boss = new NioEventLoopGroup(1); //参数1为线程数 EventLoopGroup worker = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(boss, worker) .channel(OioServerSocketChannel.class) .localAddress(new InetSocketAddress(port)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(buf.duplicate()).addListener(ChannelFutureListener.CLOSE); } }); } }); ChannelFuture f = bootstrap.bind().sync(); f.channel().closeFuture().sync(); } finally { boss.shutdownGracefully().sync(); worker.shutdownGracefully().sync(); } } //主函数 public static void main(String[] args) throws Exception{ NettyOioServer server = new NettyOioServer(); server.server(8000); } }
2.传输api
netty传输api的核心接口是Channel;
Channel表示一个连接;
Channel接口继承自Comparable接口,用来实现消息被按顺序处理;
每个Channel都会被分配一个ChannelPipeline和一个ChannelConfig;
ChannelConfig ->配置Channel,允许在运行期间更新;比如有些特定的传输有独特的配置时,需要实现一个ChannelConfig 的子类;
ChannelPipeline ->ChannelHandler的容器;
1)管道ChannelPipeline
1】ChannelHandler
ChannelPipeline 是消息处理器ChannelHandler的容器;
ChannelHandler用来处理入站出站数据和事件;
ChannelHandler常用功能:
传输数据时,将数据从一种格式转换到另一种格式
异常通知
Channel 变为 active(活动) 或 inactive(非活动) 时获得通知
Channel 被注册到EventLoop时或从EventLoop注销时获得通知
提供有关用户自定义事件的通知
2】Intercepting Filter(拦截过滤器)
ChannelPipeline实现了常用的 Intercepting Filter(拦截过滤器) 设计模式 ;
拦截过滤器模式就是对应用程序的请求或响应做一些预处理或者后续处理;
例如:在请求中写操作完成时做通知;
Channel channel = ...; // 获取channel的引用 ByteBuf buf = Unpooled.copiedBuffer("your data", CharsetUtil.UTF_8); //1.创建 ByteBuf 保存写的数据 ChannelFuture cf = channel.writeAndFlush(buf); //2.写数据,并刷新 cf.addListener(new ChannelFutureListener() { //3.添加 ChannelFutureListener 即可写操作完成后收到通知 @Override public void operationComplete(ChannelFuture future) { if (future.isSuccess()) { //4.写操作没有错误完成 System.out.println("Write successful"); } else { System.err.println("Write error"); //5.写操作完成时出现错误 future.cause().printStackTrace(); }
2)Channel的方法
3)Channel的线程安全
Channel 是线程安全的,可以被多个不同的线程安全的操作
例如:多个线程使用同一个channel的引用时,不需要考虑线程安全的问题
final Channel channel = ...; // 获取channel的引用 final ByteBuf buf = Unpooled.copiedBuffer("your data", CharsetUtil.UTF_8).retain(); Runnable writer = new Runnable() { @Override public void run() { channel.writeAndFlush(buf.duplicate()); } }; Executor executor = Executors.newCachedThreadPool(); //写进一个线程 executor.execute(writer); //写进另外一个线程 executor.execute(writer);