热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

netty源码解解析(4.0)11ChannelNIO实现概览

结构设计Channel的NIO实现位于io.netty.channel.nio包和io.netty.channel.socket.nio包中,其中io.netty.channel.

netty源码解解析(4.0)-11 Channel NIO实现-概览

    结构设计

  Channel的NIO实现位于io.netty.channel.nio包和io.netty.channel.socket.nio包中,其中io.netty.channel.nio是抽象实现,io.netty.channel.socket.nio最终实现。下面是Channel NIO相关类的派生图:

  NIO实现最终派生出3个类型NioServerSocketChannel实现了tcp server, NioSocketChannel实现了tcp client, NioDatagramChannel实现了udp socket。

  整个NIO实现分为三个层次:

  

  AbstractNioChannel抽象层

  对channel进行基本的初始化工作,把channel设置成非阻塞模式。

  实现Channel.Unsafe的connect方法框架,提供给了doConnection, doFinishConnect两个抽象方法,把真正的连接操作交给子类实现。

  覆盖了AbstractChannel的doRegister,doDeregister方法,正两个方法实现了Channel的selectionKey的注册和注销。

  实现AbstractChannel的doClose, 这个方法并没有真正关闭channel动作。

  形如doXXX的方法是,AbstractChannel提供的扩展点,在<>的末尾,给出了这些扩展点的详细列表。

  

  AbstractNioByteChannel, AbstractNioMessageChannel抽象层

  这两个类主要实现read和write的框架,它们的实现大致相同AbstractNioByteChannel读写的是byte array,而AbstractNioMessageChannel读的时候会把byte array转换成结构化的对象,写的时候把结构化对象序列化成byte array。

  AbstractNioByteChannel定义了3个抽象方法用来实现真正的读写操作: doReadBytes, doWriteBytes, doWriteFileRegion。

  AbstractNioMessageChannel第了两个2个抽象方法用来实现真正的结构化数据类型的读写: doReadMessages, doWriteMessage。

 

  NioServerSocketChannel, NioSocketChannel, NioDatagramChannel最终实现

  封装NIO API调用,真正的I/O操操作和socket相关的api调用都在这一层实现。  

 

  使用方式

  使用过netty的人都知道,netty提供了ServerBootstrap和Bootstrap类帮助用户方便地创建服务器端和客户端应用,但这不是必须的。仅仅使用NioServerSocketChannel, NioSocketChannel, NioDatagramChannel和NioEventLoopGroup就可以用开发tcp的server和client, 及udp应用。

  为了能让读者能够更清晰地理解NioEventLoopGroup和Channel直接的关系,下面给出了最原始的使用使用netty框架的代码。

  tcp server实现

 1 import io.netty.buffer.ByteBuf;
 2 import io.netty.channel.*;
 3 import io.netty.channel.nio.NioEventLoopGroup;
 4 import io.netty.channel.socket.nio.NioServerSocketChannel;
 5 
 6 import java.net.InetSocketAddress;
 7 import java.nio.charset.Charset;
 8 
 9 public class TcpServer {
10     private NioEventLoopGroup group = new NioEventLoopGroup();
11 
12     public static void main(String[] argc){
13         TcpServer server = new TcpServer();
14         server.start();
15 
16         while(true){
17             try{
18                 Thread.sleep(1000);
19             }catch (Exception e){
20                 break;
21             }
22         }
23 
24         server.stop();
25     }
26 
27     public void start(){
28         NioServerSocketChannel server = new NioServerSocketChannel();
29 
30         ChannelPipeline pipleline = server.pipeline();
31         pipleline.addFirst(new ServerHandler());
32 
33         group.register(server).addListener(new ChannelFutureListener() {
34             @Override
35             public void operationComplete(ChannelFuture future) throws Exception {
36                 server.bind(new InetSocketAddress(9001));
37                 System.out.println("server listen add:"+9001);
38             }
39         });
40     }
41     public void stop(){
42         group.shutdownGracefully();
43     }
44 
45     private class ServerHandler extends ChannelInboundHandlerAdapter{
46 
47         @Override
48         public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{
49             Channel child = (Channel)msg;
50 
51             child.pipeline().addLast(new ChildHandler());
52 
53             group.register(child);
54 
55         }
56     }
57 
58     private class ChildHandler extends ChannelInboundHandlerAdapter{
59 
60         @Override
61         public void channelActive(ChannelHandlerContext ctx) throws Exception {
62             System.out.println("connected");
63         }
64 
65         @Override
66         public void channelInactive(ChannelHandlerContext ctx) throws Exception {
67             System.out.println("closed");
68         }
69 
70         @Override
71         public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{
72             Channel chnl = ctx.channel();
73             ByteBuf data = (ByteBuf)msg;
74 
75             System.out.println("recv: "+data.toString(Charset.forName("utf-8")));
76             chnl.write(msg);
77         }
78 
79         @Override
80         public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
81             ctx.channel().flush();
82         }
83 
84     }
85 }

 

  一个channel创建之后,首先要做的事就是向pipleline中添加handler,然后才是把它注册到NioEventLoopGroup中(第31,33行)。这个顺序不能错,否则,handler的handlerAdded,channelRegistered和channelActive将不会被调用。当NioServerSocketChannel收到一个连接时,ServerHandler的的channelRead方法将会被调用,的新建好的连接当成参数传递进来,第49-53行是对新连接的初始化代码。

  tcp client实现

import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;

public class TcpClient {

    public static void main(String[] args){
        NioEventLoopGroup group = new NioEventLoopGroup();

        NioSocketChannel client = new NioSocketChannel();
        client.pipeline().addLast(new ClientInboundHandler());

        group.register(client);

        client.connect(new InetSocketAddress(9001));

        try{
            Thread.sleep(3000);
        }catch (Exception e){

        }
        group.shutdownGracefully();
    }

    private static class ClientInboundHandler extends ChannelInboundHandlerAdapter{
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("connected");
            Channel chnl = ctx.channel();
            ByteBuf buf = chnl.alloc().buffer();
            buf.writeBytes( "this is test".getBytes());
            chnl.writeAndFlush(buf);
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("closed");
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{
            ByteBuf data = (ByteBuf)msg;
            System.out.println("recv: "+data.toString(Charset.forName("utf-8")));
            ctx.channel().close();
        }
    }

}

  client的实现比server实现相对简单,添加handler,register顺序和server一致。只有把一个channel注册到gruop中之后才能调用它的方法,应为channel的大多数方法都需要通过pipleline调用,而pipleline需要在eventLoop中执行。

  udp没有server和client的区别,这里为了使代码更加清晰,把server和client代码区分开来。

  udp server

import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;

public class UdpServer {

    public static void main(String[] args){
        NioEventLoopGroup group = new NioEventLoopGroup();

        NioDatagramChannel chnl = new NioDatagramChannel();
        chnl.pipeline().addLast(new UdpHandler());

        group.register(chnl).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                chnl.bind(new InetSocketAddress(9002));
                System.out.println("udp bind at:"+9002);
            }
        });

        while(true){
            try{
                Thread.sleep(1000);
            }catch (Exception e){
                break;
            }
        }
        group.shutdownGracefully();
    }
    private static class UdpHandler extends ChannelInboundHandlerAdapter {

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.print("udp channel active");
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{
            Channel chnl = ctx.channel();
            DatagramPacket pkg = (DatagramPacket)msg;
            ByteBuf content = pkg.content();
            InetSocketAddress from = pkg.sender();
            System.out.println("recv: "+content.toString(Charset.forName("utf-8"))+" from:"+from.toString());
            pkg = new DatagramPacket(content, from);
            chnl.write(pkg);
        }

        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            ctx.channel().flush();
        }
    }
}

  udp client

import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;

public class UdpClient {

    public static void main(String[] args){
        NioEventLoopGroup group = new NioEventLoopGroup();

        NioDatagramChannel chnl = new NioDatagramChannel();
        chnl.pipeline().addLast(new UdpHandler());

        group.register(chnl).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                chnl.bind(new InetSocketAddress(0));
            }
        });

        try{
            Thread.sleep(3000);
        }catch (Exception e){
        }
        group.shutdownGracefully();
    }

    private static class UdpHandler extends ChannelInboundHandlerAdapter {

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.print("udp channel active");
            Channel chnl = ctx.channel();
            ByteBuf content = chnl.alloc().buffer();
            content.writeBytes("udp message".getBytes());
            chnl.writeAndFlush(new DatagramPacket(content, new InetSocketAddress("127.0.0.1", 9002)));
            System.out.println("send message");
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{
            DatagramPacket pkg = (DatagramPacket)msg;
            ByteBuf content = pkg.content();
            InetSocketAddress from = pkg.sender();
            System.out.println("recv: "+content.toString(Charset.forName("utf-8"))+" from:"+from.toString());
        }
    }
}

  NioDatagramChannel和NioSocketChannel的初始化过程大致相同。它们的不同点是,NioSocketChannel在connect之后处于active状态,NioDatagramChannel是在bind之后处于才处于active状态。

  

  

  

posted on 2019-01-15 13:45 自带buff 阅读(...) 评论(...) 编辑 收藏


推荐阅读
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 本文介绍了如何清除Eclipse中SVN用户的设置。首先需要查看使用的SVN接口,然后根据接口类型找到相应的目录并删除相关文件。最后使用SVN更新或提交来应用更改。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • Netty源代码分析服务器端启动ServerBootstrap初始化
    本文主要分析了Netty源代码中服务器端启动的过程,包括ServerBootstrap的初始化和相关参数的设置。通过分析NioEventLoopGroup、NioServerSocketChannel、ChannelOption.SO_BACKLOG等关键组件和选项的作用,深入理解Netty服务器端的启动过程。同时,还介绍了LoggingHandler的作用和使用方法,帮助读者更好地理解Netty源代码。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 安卓select模态框样式改变_微软Office风格的多端(Web、安卓、iOS)组件库——Fabric UI...
    介绍FabricUI是微软开源的一套Office风格的多端组件库,共有三套针对性的组件,分别适用于web、android以及iOS,Fab ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
author-avatar
当时桃花恨春风_375
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有