ZeroMQ(java)中对IO的封装(StreamEngine)
哎,各种各样杂七杂八的事情。。。好久没有看代码了,其实要搞明白一个与IO相关的框架,最好的办法就是把它的I/0的读写两个过程搞清楚。。。例如在netty中,如果能将eventLoop的运行原理搞清楚,然后摸清楚整个I/O读写两个过程,那么也就差不太多了。。。。
这次来看看ZeroMQ(java)中如何来处理I/O的,先来看看一个类型的定义,IOObject类型,这个类型应该扮演的是工具类的形象,前面看过在ZeroMQ中所谓的IO线程的定义,那么IOObject就是用于直接与IO线程交互的,或者说的更直接的一点就是它是与IO线程里的poller对象交互的。。。
那么先来看看IOObject的类图吧:
这张图应该将IOObject与IOThread以及Poller之间的关系表现的很清楚了吧。。。。IOObject实现了IPollEvents接口,那么也就代表它可以响应IO事件。。。不过其实它并不直接实现这些IO事件,而是将其委托给内部的一个IPollEvents对象。。只不过是做了一层代理而已。。。
好了,接下来来看看IOObject的代码吧,先来看看它的属性申明:
- private Poller poller; //poller对象
- private IPollEvents handler; //用于执行事件回调的handler
这个poller就是从IO线程里面获取过来的,handler就是刚刚提到的事件回调的处理对象。。。IOObject不过是对其进行了一层包装而已。。。
那么接下来来看看重要的方法定义:
- //在将一个IO对象加入到一个IO线程的时候,要注意确定当前IO对象之前没有加入到任何IO线程或者已经从别的IO线程上面退下来了
- //将当前这个IO对象加入到IO线程上面去,说白了主要是获取这个IO线程的poller对象
- public void plug(IOThread io_thread_) {
- assert (io_thread_ != null);
- assert (poller == null);
- poller = io_thread_.get_poller (); //获取这个线程的poller对象
- }
这个方法用于将当前这个IO对象加入到一个IO线程,其实主要的是要获取这个IO线程的Poller对象。。好了,接下来再来看看如何注册channel以及事件吧:
- //在poller里面移除channel
- public final void rm_fd(SelectableChannel handle) {
- poller.rm_fd(handle);
- }
- //给这个channel注册读取的事件
- public final void set_pollin (SelectableChannel handle_) {
- poller.set_pollin (handle_);
- }
- //在这个channel上面注册写事件
- public final void set_pollout (SelectableChannel handle_) {
- poller.set_pollout (handle_);
- }
- //注册链接事件
- public final void set_pollconnect(SelectableChannel handle) {
- poller.set_pollconnect(handle);
- }
- //注册accept事件
- public final void set_pollaccept(SelectableChannel handle) {
- poller.set_pollaccept(handle);
- }
- //取消读取事件的注册
- public final void reset_pollin(SelectableChannel handle) {
- poller.reset_pollin (handle);
- }
- //取消写事件的注册
- public final void reset_pollout(SelectableChannel handle) {
- poller.reset_pollout (handle);
- }
这部分代码应该很简单吧,而且应该对IOObject的用处比较的清楚了,然后至于说IOObject对象如何响应in_event什么的,前面已经说过了,其实是委托给了handler对象来处理。。。好啦,IOObject的分析就算差不多了。。接下来来看看StreamEngine类型的实现吧,还是先来看看它初略的类图吧:
其实觉得看一个类的类图,基本上就能看出这个类的很多情况,好了,不说闲话了,来看看它的属性的定义吧:
- private static final int GREETING_SIZE = ; //问候msg的大小,12个字节 (10字节的头,1字节的版本,1字节的socket类型)
- // True iff we are registered with an I/O poller.
- private boolean io_enabled; //如果是true的话,表示当前已经注册到了poller上面去
- private SocketChannel handle; //真正底层用于通信的socketChannel
- private ByteBuffer inbuf; //接收数据的buf
- private int insize; //记录接收的数据的大小
- private DecoderBase decoder; //decoder
- private Transfer outbuf; //outbuf
- private int outsize; //outbuf的大小
- private EncoderBase encoder; //encoder
- // When true, we are still trying to determine whether
- // the peer is using versioned protocol, and if so, which
- // version. When false, normal message flow has started.
- private boolean handshaking; //是否是在握手中,当值为false的时候代表握手已经完成了
- // The receive buffer holding the greeting message
- // that we are receiving from the peer.
- private final ByteBuffer greeting; //用于接收问候msg的buf
- // The send buffer holding the greeting message
- // that we are sending to the peer.
- private final ByteBuffer greeting_output_buffer; //用于发送问候msg的buf
- private SessionBase session; //所属的session
- private Options options; //选项配置
- // String representation of endpoint
- private String endpoint; //这里一般是地址信息
- private boolean plugged; //是否已经加入了
- private boolean terminating; //是否已经停止了
- // Socket
- private SocketBase socket; //所属的socket
- private IOObject io_object; //拥有的IO对象
这里面有很多重要的属性,例如handler是SocketChannel类型的,可以知道它才是实际上底层用于通信的,然后又inbuf以及outbuf,这两个东西是干嘛用的应该一眼就看出来了吧,然后还有encoder和decoder,呵呵,可以猜到,读取到的数据先要经过decoder的处理才提交给上层,发送出去的数据也会通过encoder处理成二进制再发送出去。。。然后还有一个io_objcet对象。。。
接下来来看看构造方法吧:
- //构造函数,第一个参数是底层的channel,
- public StreamEngine (SocketChannel fd_, final Options options_, final String endpoint_)
- {
- handle = fd_;
- inbuf = null;
- insize = ;
- io_enabled = false;
- outbuf = null;
- outsize = ;
- handshaking = true; //初始化为ture,表示还没有完成握手
- session = null;
- options = options_;
- plugged = false;
- terminating = false;
- endpoint = endpoint_;
- socket = null;
- greeting = ByteBuffer.allocate (GREETING_SIZE); //创建用于接收问候msg的buf
- greeting_output_buffer = ByteBuffer.allocate (GREETING_SIZE); //创建用于发送握手信息的buf
- encoder = null;
- decoder = null;
- try {
- Utils.unblock_socket (handle); //将底层的channel设置为非阻塞的
- if (options.sndbuf != ) { //设置底层的socket的发送缓冲大小
- handle.socket().setSendBufferSize((int)options.sndbuf);
- }
- if (options.rcvbuf != ) { //设置底层的socket的接收缓冲大小
- handle.socket().setReceiveBufferSize((int)options.rcvbuf);
- }
- } catch (IOException e) {
- throw new ZError.IOException(e);
- }
- }
这个比较有意思的就是将channel设置为了非阻塞的模式,然后设置了底层socket的发送以及接受缓冲的大小。。其余的就没啥意思了。。。
- //将当前engine加入到IO线程以及session,其实这里最主要的事情是将channel注册到poller上面去
- public void plug (IOThread io_thread_,
- SessionBase session_) {
- assert (!plugged);
- plugged = true; //标志位
- // Connect to session object.
- assert (session == null);
- assert (session_ != null);
- session = session_; //当前所属的session
- socket = session.get_soket (); //获取所属的scoekt,这个是ZMQ的socket
- io_object = new IOObject(null); //创建IO对象,
- io_object.set_handler(this); //设置IO对象的事件回调
- // Connect to I/O threads poller object.
- io_object.plug (io_thread_); // 将IO对象搞到这个IO线程上面去,其实最主要的就是获取这个IO线程的poller对象
- io_object.add_fd (handle); //将底层的channel加入
- io_enabled = true; //表示已经加入了
- // Send the 'length' and 'flags' fields of the identity message.
- // The 'length' field is encoded in the long format.
- //设置发送的问候msg的信息
- greeting_output_buffer.put ((byte) );
- greeting_output_buffer.put ((byte) ) { //如果inbuf里面没有数据需要处理
- // Retrieve the buffer and read as much data as possible.
- // Note that buffer can be arbitrarily large. However, we assume
- // the underlying TCP layer has fixed buffer size and thus the
- // number of bytes read will be always limited.
- inbuf = decoder.get_buffer (); //从解码器里面获取buf,用于写入读取的数据,因为在已经设置了底层socket的TCP接收缓冲区的大小
- insize = read (inbuf); //用于将发送过来的数据写到buf中去,并记录大小
- inbuf.flip(); //这里准备从buf里面读取数据了
- // Check whether the peer has closed the connection.
- if (insize == -) { //如果是-1的话,表示底层的socket连接已经出现了问题
- insize = ;
- disconnection = true;
- }
- }
- // Push the data to the decoder.
- int processed = decoder.process_buffer (inbuf, insize); //解析这些读取到的数据
- if (processed == -) {
- disconnection = true;
- } else {
- // Stop polling for input if we got stuck.
- if (processed < insize) //如果处理的数据居然还没有读到的数据多,那么取消读取事件的注册
- io_object.reset_pollin (handle);
- // Adjust the buffer.
- insize -= processed; //还剩下没有处理的数据的大小
- }
- // Flush all messages the decoder may have produced.
- session.flush (); //将decoder解析出来的数据交给session
- // An input error has occurred. If the last decoded message
- // has already been accepted, we terminate the engine immediately.
- // Otherwise, we stop waiting for socket events and postpone
- // the termination until after the message is accepted.
- if (disconnection) { //表示已经断开了连接,那么需要处理一下
- if (decoder.stalled ()) {
- io_object.rm_fd (handle);
- io_enabled = false;
- } else
- error ();
- }
- }
- //表示可以写数据了
- public void out_event () {
- // If write buffer is empty, try to read new data from the encoder.
- if (outsize == ) { //需要写的数据量为0
- // Even when we stop polling as soon as there is no
- // data to send, the poller may invoke out_event one
- // more time due to 'speculative write' optimisation.
- if (encoder == null) {
- assert (handshaking);
- return;
- }
- outbuf = encoder.get_data (null); //从encoder里面获取数据
- outsize = outbuf.remaining();
- // If there is no data to send, stop polling for output.
- if (outbuf.remaining() == ) { //如果确实没有数据要写,那么取消写事件的注册
- io_object.reset_pollout (handle);
- // when we use custom encoder, we might want to close
- if (encoder.is_error()) {
- error();
- }
- return;
- }
- }
- // If there are any data to write in write buffer, write as much as
- // possible to the socket. Note that amount of data to write can be
- // arbitratily large. However, we assume that underlying TCP layer has
- // limited transmission buffer and thus the actual number of bytes
- // written should be reasonably modest.
- int nbytes = write (outbuf); //写数据
- // IO error has occurred. We stop waiting for output events.
- // The engine is not terminated until we detect input error;
- // this is necessary to prevent losing incomming messages.
- if (nbytes == -) { //如果-1,那么表示底层用到的socket其实已经出现了问题
- io_object.reset_pollout (handle); //取消写事件的注册
- if (terminating)
- terminate ();
- return;
- }
- outsize -= nbytes; //这里更新需要写的数据的数量
- // If we are still handshaking and there are no data
- // to send, stop polling for output.
- if (handshaking)
- if (outsize == )
- io_object.reset_pollout (handle);
- // when we use custom encoder, we might want to close after sending a response
- if (outsize == ) {
- if (encoder != null && encoder.is_error ()) {
- error();
- return;
- }
- if (terminating)
- terminate ();
- }
- }
这两个方法是用于相应IO事件的,前面提到的IOObject将IO事件其实委托给了内部的handler来处理,其实这个handler对象就是SteamEngine对象,也就是底层的channel有数据可以读写的时候,将会用上面的两个方法来处理。这里就可以看到读写事件最原始的处理流程了,而且也看到了encoder以及decoder的用处。。。这里代码应该还算是比较的简单,由于这部分还涉及到与上层的session对象之间的交互,这个还要等到以后来分析。。。
好了,那么到这里ZeroMQ中IO的处理流程也就算是有了基本的了解了。。。。
- 顶
ZeroMQ(java)中对IO的封装(StreamEngine)的更多相关文章
- ZeroMQ(java)中的数据流SessionBase与SocketBase
前面的文章中已经比较的清楚了ZeroMQ(java)中如何在底层处理IO, 通过StreamEngine对象来维护SelectableChannel对象以及IO的事件回调,然后通过Poller对象来维 ...
- ZeroMQ(JAVA)中的数据流,SessionBase与SocketBase
前面的文章中已经比较的清楚了ZeroMQ(java)中如何在底层处理IO, 通过StreamEngine对象来维护SelectableChannel对象以及IO的事件回调,然后通过Poller对象来维 ...
- java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET
java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET 亲,“社区之星”已经一周岁了! 社区福利快来领取免费参加MDCC大会机会哦 Tag功能介绍—我们 ...
- 深入理解Java中的IO
深入理解Java中的IO 引言: 对程序语言的设计者来说,创建一个好的输入/输出(I/O)系统是一项艰难的任务 < Thinking in Java > 本文的目录视图如下: ...
- Java中的IO流,Input和Output的用法,字节流和字符流的区别
Java中的IO流:就是内存与设备之间的输入和输出操作就成为IO操作,也就是IO流.内存中的数据持久化到设备上-------->输出(Output).把 硬盘上的数据读取到内存中,这种操作 成为 ...
- Java中的IO流(五)
上一篇<Java中的IO流(四)>记录了一下Properties类,此类不属于IO流,它属于集合框架.接下来说一下IO流中的其它流 一,打印流PrintStream PrintStream ...
- Java中的IO流(三)
上一篇<Java中的IO流(二)>把学习Java的字符流以及转换流作了一下记录,从本篇开始将把IO流中对文件或文件夹操作的对象File类的学习进行一下记录. 一,File类的构造函数及字段 ...
- Java中的IO
引言: 对程序语言的设计者来说,创建一个好的输入/输出(I/O)系统是一项艰难的任务 < Thinking in Java > 本文的目录视图如下: Java IO概要 a ...
- java中的IO操作总结
一.InputStream重用技巧(利用ByteArrayOutputStream) 对同一个InputStream对象进行使用多次. 比如,客户端从服务器获取数据 ,利用HttpURLConnect ...
- java中的IO流
Java中的IO流 在之前的时候我已经接触过C#中的IO流,也就是说集中数据固化的方式之一,那么我们今天来说一下java中的IO流. 首先,我们学习IO流就是要对文件或目录进行一系列的操作,那么怎样操 ...
随机推荐
- (旧)子数涵数·PS——冷色调与LOMO
一.准备素材(我是从百度图库里下载的) 二.打开PS和素材 三.复制图层,快捷键Ctrl+J,并把原图层隐藏,只在副本上编辑(好习惯) 四.使用"匹配颜色"命令,增加"明 ...
- 关于hangfire的使用
hangfire 是一个分布式后台执行服务.用它可以代替ThreadPool.QueunItemWork等原生方法.当然4.5后的 task也是相当好用且功能强大.不过如果想分布式处理并且可监控的话, ...
- 世界上最小的发行版之一Tiny Core
Tiny Core Linux不足之处驱动不给力 Tiny Core是一个简单的范例来说明核心项目可以提供什么.它提供了一个12MB的FLTK/FLWM桌面.用户对提供的程序和外加的硬件有完整的控制权 ...
- 2016 版 Laravel 系列入门教程(一)【最适合中国人的 Laravel 教程】
本教程示例代码见: https://github.com/johnlui/Learn-Laravel-5 在任何地方卡住,最快的办法就是去看示例代码. 本文基于 Laravel 5.2 版本,无奈 5 ...
- python 2.7的安装
最近准备入手学习python 这里我是按照:http://blog.csdn.net/jcjc918/article/details/11022345 来的 我在安装python 3 的时候发现上下左 ...
- iOS边练边学--文件压缩和解压缩的第三方框架SSZipArchive的简单使用
一.非cocoaPods方法,需要注意的是:直接将SSZipArchive拖入项目编译会报错. Undefined symbols for architecture x86_64: "_cr ...
- .map文件的作用以及在chorme下会报错找不到jquery-1.10.2.min.map文件,404 的原因
source map文件是js文件压缩后,文件的变量名替换对应.变量所在位置等元信息数据文件,一般这种文件和min.js主文件放在同一个目录下. 比如压缩后原变量是map,压缩后通过变量替换规则可能会 ...
- BZOJ1001 狼抓兔子(裸网络流)
Description 现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的, 而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一 ...
- c中动态使用数组
#include <iostream> #include <fstream> #include<stdlib.h> #define MAXNUM 200 int I ...
- 浏览器查看cookie
今天总结下,教你怎样查看一些浏览器的Cookie,比如IE.Firefox.Chrome的Cookies等.下面分块介绍,以后会关注一些没有讲到的浏览器获取Cookie的方法. 1.Firefox浏览 ...
