Java session write

基于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.   
  5.     @Override  
  6.     public ProtocolEncoder getEncoder(IoSession session) throws Exception {  
  7.         return encoder;  
  8.     }  
  9.   
  10.   
  11.     @Override  
  12.     public ProtocolDecoder getDecoder(IoSession session) throws Exception {  
  13.           
  14.         return decoder;  
  15.     }  
  16.   
  17. }  

三、分析  
进入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.   
  5.         // Then, get the chain and inject the WriteRequest into it  
  6.         IoFilterChain filterChain = getFilterChain();  
  7.         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.   
  3.     /** The chain head */  
  4.     private final EntryImpl head;  
  5.   
  6.     /** The chain tail */  
  7.     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.   
  19.             s.getWriteRequestQueue().offer(s, writeRequest);  
  20.             if (!s.isWriteSuspended()) {  
  21.                 s.getProcessor().flush(s);  
  22.             }  

WriteRequestQueue的默认实现就是java.util.concurrent.ConcurrentLinkedQueue,舍去传入的session对象。 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,Session对象可以通过序列化来保存和恢复。Session是服务器端的对象,存储在服务器的内存中,而客户端只保存了一个Session ID。当客户端发起请求时,服务器根据Session ID来获取相应的Session对象,并将其反序列化为Java对象。这样就可以在不同的请求之间共享Session对象。 要将Session对象序列化,需要确保Session对象中所有的属性都是可序列化的。通常来说,Session中存储的数据只能是简单的数据类型,如字符串、数字等,这些数据类型都是可序列化的。如果Session中存储了自定义的对象,则需要将这些对象也实现序列化接口,才能将Session对象序列化。 在Java中,可以使用Java自带的序列化机制来实现Session对象的序列化。具体来说,可以将Session对象写入到文件或者网络中,然后再从文件或者网络中读取Session对象。在写入和读取的过程中,需要使用Java的ObjectOutputStream和ObjectInputStream类。其中,ObjectOutputStream类用于将对象序列化为字节流,而ObjectInputStream类则用于将字节流反序列化为对象。 以下是一个简单的示例代码,演示如何将Session对象序列化为字节流并写入到文件中: ```java // 创建Session对象 Session session = request.getSession(); // 将Session对象序列化为字节流 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(session); byte[] bytes = baos.toByteArray(); // 将字节流写入到文件中 FileOutputStream fos = new FileOutputStream("session.ser"); fos.write(bytes); fos.close(); ``` 反序列化的过程与上述写入过程类似,只需要将字节流读取出来,并使用ObjectInputStream类将其反序列化为Session对象即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值