基于Mina开发网络通信程序,在传感器数据接入领域应用的很广泛,今天我无意中发现一个问题,那就是我在前端session.write(msg)数据出去之后,却没有经过Filter的Encoder方法,同样能够写入远程服务器。因为我所发送的数据不需要很复杂的编码,所以encoder方法也一直没有去看,今天发现无法被自己写的过滤器所编码,针对这个问题,我打开以前的代码以及以前的项目中的相关代码,有些同事也是session.write(IoBuffer)之后,在encoder方法里面还加上了一句out.write(message);通过跟踪Mina源码发现,session写出去的数据类型是IoBuffer格式的,就不经过自定义的过滤器了。所以下面的代码压根是多余的

Java代码  
  1. @Override
  2. public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
  3. out.write(message);//IoBuffer格式写出去之后,跳过了encoder.
  4. }

下面我把自己跟踪调试Mina的过程记录下来. 
一、场景  
客户端需要每隔Time时间向服务端发送心跳包,代码如下: 
session.write(IoBuffer.wrap("心跳包XXX".getBytes())); 
二、现象 
MyFilter中的Encoder方法encoder不执行

Java代码  
  1. public class MyFilter implements ProtocolCodecFactory {
  2. private ProtocolEncoder encoder = new MyEncoder();
  3. private ProtocolDecoder decoder = new MyDecoder();
  4. @Override
  5. public ProtocolEncoder getEncoder(IoSession session) throws Exception {
  6. return encoder;
  7. }
  8. @Override
  9. public ProtocolDecoder getDecoder(IoSession session) throws Exception {
  10. return decoder;
  11. }
  12. }

三、分析  
进入session.write方法,实现IoSession.write方法的是AbstractIoSession。直接调用的是

Java代码  
  1. public WriteFuture write(Object message) {
  2. return write(message, null);
  3. }

而AbstractIoSession.write(Object message, SocketAddress address) 
该方法的工作流程是:

  • 创建WriteFeature对象,用于返回值(session.write本身就是返回writeFeature)
  • 将session.write(message)中的Object类型的message封装成writeRequest.
  • 启动write动作,这个主要是IoFilterChain来完成的。

具体的核心代码如下:

Java代码  
  1. // Now, we can write the message. First, create a future
  2. WriteFuture writeFuture = new DefaultWriteFuture(this);
  3. WriteRequest writeRequest = new DefaultWriteRequest(message, writeFuture, remoteAddress);
  4. // Then, get the chain and inject the WriteRequest into it
  5. IoFilterChain filterChain = getFilterChain();
  6. filterChain.fireFilterWrite(writeRequest);

继续跟踪到fireFilterWrite里面去,可知IoFilterChain的默认实现类DefaultIoFilterChain中的关键方法:

Java代码  
  1. public void fireFilterWrite(WriteRequest writeRequest) {
  2. Entry tail = this.tail;
  3. callPreviousFilterWrite(tail, session, writeRequest);
  4. }

在这里先要介绍一下DefaultIoFiterChain的数据格式,主要的属性如下:

Java代码  
  1. private final Map<String, Entry> name2entry = new ConcurrentHashMap<String, Entry>();
  2. /** The chain head */
  3. private final EntryImpl head;
  4. /** The chain tail */
  5. private final EntryImpl tail;

其中 head与tail都是DefaultIoFilterChain固有的属性,name2entity是我们为FilterChain添加的过滤器。因而IoFilterChain是用一个链表来保存过滤器的(('tail', prev: 'myFilter:ProtocolCodecFilter', next: 'null')),其中表头和表位都是固定的head和tail,他们对应的Filter也是专有的,HeadFilter和TailFilter. 
关键方法是callPreviousFilterWrite(tail, session, writeRequest);

Java代码  
  1. try {
  2. IoFilter filter = entry.getFilter();
  3. NextFilter nextFilter = entry.getNextFilter();
  4. filter.filterWrite(nextFilter, session, writeRequest);
  5. } catch (Throwable e) {
  6. writeRequest.getFuture().setException(e);
  7. fireExceptionCaught(e);
  8. }

从上面两个代码片段中,可以看出,IoFilterChain首先从列表中找到tail,从tail开始查找filter,顺序调用每个filter的filterWrite()方法。这里的‘顺序调用’,指的是从tail->head调用,也就是逆向调用Filter。但是看到filter.filterWrite(nextFilter, session, writeRequest);这行代码中的参数可以发现,nextFilter,表面的意思是下一个过滤器,有点误解,感觉tail下一个过滤器不就是null吗,其实不然,进入filterWriter可知。

Java代码  
  1. Entry nextEntry = EntryImpl.this.prevEntry;
  2. callPreviousFilterWrite(nextEntry, session, writeRequest);

对于除head和tail过滤器外,其他的过滤器是如何工作的呢?我们看看ProtocolCodecFilter中的fireFilter方法,做了这样的处理:

Java代码  
  1. if ((message instanceof IoBuffer) || (message instanceof FileRegion)) {
  2. nextFilter.filterWrite(session, writeRequest);
  3. return;
  4. }

到这里,就明白了为什么session.write(IoBuffer.wrap())这样写出去,无法经过自己定义的过滤器了,原来在fireFilter中,对message做了判断,如果已经是IoBuffer类型的,就直接return了。 
最后执行的是HeadFilter的fireFilter方法,直接看内容:

Java代码  
  1. if (writeRequest.getMessage() instanceof IoBuffer) {
  2. IoBuffer buffer = (IoBuffer) writeRequest.getMessage();
  3. // I/O processor implementation will call buffer.reset()
  4. // it after the write operation is finished, because
  5. // the buffer will be specified with messageSent event.
  6. buffer.mark();
  7. int remaining = buffer.remaining();
  8. if (remaining == 0) {
  9. // Zero-sized buffer means the internal message
  10. // delimiter.
  11. s.increaseScheduledWriteMessages();
  12. } else {
  13. s.increaseScheduledWriteBytes(remaining);
  14. }
  15. } else {
  16. s.increaseScheduledWriteMessages();
  17. }
  18. s.getWriteRequestQueue().offer(s, writeRequest);
  19. if (!s.isWriteSuspended()) {
  20. s.getProcessor().flush(s);
  21. }

WriteRequestQueue的默认实现就是java.util.concurrent.ConcurrentLinkedQueue,舍去传入的session对象。

Java session write相关推荐

  1. java session时间_java session时长问题,java设置session超时时间实例

    java session超时设置你知道应该如何设置吗?下面要给大家带来的实例就是和java设置session超时时间相关的内容,一起来看看具体实现方式吧. 一般的系统登陆了之后,都会有设置一个当前的s ...

  2. php java session共享_php 函数session_id()思考。实现同服务器下session共享

    session_id() session_id() 存取目前 session 代号. 语法: string session_id(string [id]); 本函数可取得或者重新配置目前存放 Sess ...

  3. java session缓存_Java服务端采用Session的缓存oauth2.0授权用户信息

    前面有讲到session和cookie的一些简单的区别和比较,我们继续为java在服务端如何对session的操作留下笔记,这里以用户的登陆和退出操作为实用场景,简单讲解session的建立.清空等操 ...

  4. java session验证码_利用session实现一次性验证码

    带有验证码的登录页面 用户名: 密码: 验证码: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; impo ...

  5. java session使用_使用Neo4j和Java进行大数据分析 第2部分

    本文的第一部分介绍了Neo4j及其Cypher查询语言.如果您已经阅读了第1部分,那么您已经了解了为什么Neo4j和其他图形数据库特别受社交图形或网络中用户之间关系建模的影响.您还在开发环境中安装了N ...

  6. java session创建_request创建session

    如何在 Java 中创建 session ? 使用request对象获取session,然后进行操作. 1,引入包servlet-api.jar . 2,使用request获取session:Http ...

  7. java session持久化_Session的生命周期和持久化

    ![](https://box.kancloud.cn/44537ef796e6a49c6421fd9186276df6_468x371.png) web.xml中配置,session的计算是从停止操 ...

  8. Redis保存Java Session

    2019独角兽企业重金招聘Python工程师标准>>> http://www.cnblogs.com/lengfo/p/4260363.html 一.前言 nginx 作为目前最流行 ...

  9. java session事件,Java开发网 - 再论Session事件的捕获

    最近又有网友问到,如何用Session实现在线统计的功能,其实只要对Servlet规范详细了解一下,明白其基本原理,编写一个类似的功能并不是一件很复杂的事情.我以前的一篇文章,最初也是发表在JavaR ...

  10. java session 数量_java中使用session监听实现同帐号登录限制、登录人数限制

    本文主要介绍了java中使用session监听实现同帐号登录限制.登录人数限制,具体代码如下: 问题域: 1.同帐号登录:若此帐号已登录,不可再次登录(与QQ模式相反). 2.登录人数限制,超过.已达 ...

最新文章

  1. centos5.6 (64bit)编译安装vsftpd-2.3.4的配置(两种用户登录)[连载之电子商务系统架构]...
  2. ZCF提出解决零确认交易安全问题新方案
  3. 压缩感知专题笔记——目录
  4. 白话Elasticsearch63-生产集群部署之硬件配置、jvm以及集群规划建议
  5. 【c++】5.函数传指针与传值特容易混淆的点
  6. FreeRTOS 中断优先级嵌套错误引发HardFault异常解决
  7. python3 拼接字符串的7种方法
  8. 2021年科研学术海报Poster模板
  9. ASP.NET Core的身份认证框架IdentityServer4--入门【转】
  10. java shell文件_JAVA执行bat文件和shell脚本文件
  11. CF(427D-Match amp; Catch)后缀数组应用
  12. 《深入浅出数据分析》
  13. oracle全量增量_数据上云,应该选择全量抽取还是增量抽取?
  14. Jenkins容器由于虚拟内存不足导致的异常退出
  15. linux命令测网速
  16. UVA - 12304 2D Geometry 110 in 1!
  17. 计算机清理垃圾文件丢失怎么恢复,垃圾箱清空了怎么恢复
  18. lxml中检查元素是否含有子元素时提示Use specific 'len(elem)' or 'elem is not None' test instead.暨len函数和is not None的区别
  19. 第55周收录123起融资,国内一半未披露金额,全球电商值得关注 | 潜在周报
  20. Twipstopixels java_Access量度单位缇与像素,厘米等的换算关系

热门文章

  1. jest 客户端 实现 Sliced+Scroll并行查询
  2. 如何使用 you-get 下载视频
  3. 动漫设计与制作计算机专业,计算机动漫设计与制作专业(毕业论文).doc
  4. linux 取得文件行数
  5. 每日一题 笨拙的手指
  6. html 把table固定住,html Table实现表头固定
  7. Python画皮卡丘(自创)
  8. 2018通达信l2服务器源码,分享通达信(L2) 主力资金线/主力资金流向 源码
  9. [Jzoj] 3426. 封印一击
  10. 环信网页端客服集成用户体系