mina客户端发送消息延迟问题分析
原文:http://www.cnblogs.com/haiq/archive/2011/08/01/2124292.html
(写的蛮好,保存下来)
由于项目需要,用到了 mina 框架进行 tcp 通讯。我是初次接触 mina,于是从 Hello world 开始学习了 mina 。期间遇到了一个奇怪的发送数据的延迟问题,解决的过程是曲折的,但找出的原因却令我“吐血”(没真的吐……)。不管怎样,还是贴出来一下作反面案例,希望初次学习 mina 的时候能够绕过这个地雷。
public class HelloServer { public static void main(String[] args) { IoBuffer.setUseDirectBuffer(false); IoBuffer.setAllocator(new SimpleBufferAllocator()); IoAcceptor acceptor = new NioSocketAcceptor(); acceptor.getFilterChain().addLast("logger", new LoggingFilter()); acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory( Charset.forName("UTF-8")))); acceptor.getSessionConfig().setReadBufferSize(2048); acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); acceptor.setHandler(new HelloHandler()); try { acceptor.bind(new InetSocketAddress(8800)); System.out.println("hello server is running!"); } catch (IOException e) { e.printStackTrace(); } } } |
public class HelloHandler extends IoHandlerAdapter { @Override public void sessionCreated(IoSession session) throws Exception { System.out.println("sessiong created ......"); } @Override public void sessionOpened(IoSession session) throws Exception { System.out.println("session opened ......"); } @Override public void sessionClosed(IoSession session) throws Exception { System.out.println("session closed ."); } @Override public void messageReceived(IoSession session, Object message) throws Exception { String msgText = message.toString(); System.out.println(getNow() + "message received!-- msg:" + msgText); } private String getNow(){ Date now = new Date(); DateFormat df = new SimpleDateFormat("[yyyy-MM-dd hh:mm:ss] "); return df.format(now); } @Override public void messageSent(IoSession session, Object message) throws Exception { String msgText = message.toString(); System.out.println("message sent!-- msg:" + msgText); } @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { System.out.println("exception occurred!!! -- " + cause.getMessage()); cause.printStackTrace(); session.close(false); } } |
public class HelloClient { public static void main(String[] args) { NioSocketConnector connector = new NioSocketConnector(); connector.setConnectTimeoutMillis(1000 * 15); connector.getFilterChain().addLast("logger", new LoggingFilter()); connector.getFilterChain().addLast( "codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset .forName("UTF-8")))); connector.setHandler(new HelloSender(new String[] { "Hello message 1 !", "Hello 2" })); try { ConnectFuture future = connector.connect(new InetSocketAddress( "127.0.0.1", 8800)); System.out.println("connect ..."); future.awaitUninterruptibly(); System.out.println("connect future awaitUniterruptibly ..."); IoSession session = future.getSession(); System.out.println("get session"); session.getCloseFuture().awaitUninterruptibly(); System.out.println("session close future awaitUniterruptibly ..."); connector.dispose(); } catch (Exception e) { System.out.println("error !!! --" + e.getMessage()); e.printStackTrace(); } } } |
public class HelloSender extends IoHandlerAdapter { private String[] msgArray; public HelloSender(String[] msgArray) { this.msgArray = msgArray; } @Override public void sessionOpened(IoSession session) throws Exception { SendMessage(session); session.close(false); System.out.println("client handler close session ......"); } private void SendMessage(IoSession session)throws Exception{ for (int i = 0; i < msgArray.length; i++) { WriteFuture wf = session.write(msgArray[i]); wf.addListener(new IoFutureListener<IoFuture>(){ public void operationComplete(IoFuture future) { System.out.println(getNow() + "futrue -- write completed!"); } }); System.out.println(getNow() + "--write msg " + i); Thread.sleep(3000); } System.out.println(getNow() + "send completed"); } private String getNow(){ Date now = new Date(); DateFormat df = new SimpleDateFormat("[yyyy-MM-dd hh:mm:ss] "); return df.format(now); } } |
// Now, we can write the message. First, create a future WriteFuture writeFuture = new DefaultWriteFuture(this); WriteRequest writeRequest = new DefaultWriteRequest(message, writeFuture, remoteAddress); // Then, get the chain and inject the WriteRequest into it IoFilterChain filterChain = getFilterChain(); filterChain.fireFilterWrite(writeRequest); |
s.getWriteRequestQueue().offer(s, writeRequest); if (!s.isWriteSuspended()) { s.getProcessor().flush(s); } |
((AbstractIoSession) session).getProcessor().flush(session);
connector.getSessionConfig().setTcpNoDelay(true);
运行 HelloClient ……
日志输出显示的结果还是一如既往。
NoDelay 是 TCP 层的一个选项,其指示是否将缓存区中的数据合并发送。但在此无论设置为 true 还是 false,都不影响测试的结果。
三、IoHandler 的问题
在进行了以上两方面的尝试后,将怀疑的目光转向了 HelloSender ,这是本例中的 IoHandler 的实现。
发送操作是在 HelloSender 的 sessionOpened 方法执行:
@Override public void sessionOpened(IoSession session) throws Exception { SendMessage(session); session.close(false); System.out.println("client handler close session ......"); } |
当 session 创建后,sessionOpened 便被执行,此时发送数据,并在发送完成后关闭会话的,然后,sessionOpened 方法返回。
仔细想想,似乎在 SendMessage 和 sessionOpened 方法返回之间有些问题:对 sessionOpened 的调用应该是建立连接和会话后初始化过程的一部分,而在初始化过程尚未返回的时候就对 IoSession 写入数据,这也许不是一个恰当的调用方式。也许正是这样,使得先后两次写入的数据都被保持在队列中,直到会话初始化完成后才被处理。在 sessionOpened 方法中创建一个新线程来执行 SendMessage 操作就能验证这一设想,于是 sessionOpened 方法改为如下:
@Override public void sessionOpened(IoSession session) throws Exception { final IoSession s = session; Thread thrd = new Thread(new Runnable() { public void run() { try { SendMessage(s); Thread.sleep(5000); s.close(false); } catch (Exception e) { System.out.println("Send message error!!!--" + e.getMessage()); e.printStackTrace(); } } }); thrd.start(); System.out.println("client handler close session ......"); } |
mina客户端发送消息延迟问题分析的更多相关文章
- 使用Java客户端发送消息和消费的应用
体验链接:https://developer.aliyun.com/adc/scenario/fb1b72ee956a4068a95228066c3a40d6 实验简介 本教程将Demo演示使用jav ...
- Netty客户端发送消息并同步获取结果
客户端发送消息并同步获取结果,其实是违背Netty的设计原则的,但是有时候不得不这么做的话,那么建议进行如下的设计: 比如我们的具体用法如下: NettyRequest request = new N ...
- java socket 一个服务器对应多个客户端,可以互相发送消息
直接上代码,这是网上找的demo,然后自己根据需求做了一定的修改.代码可以直接运行 服务器端: package socket; import java.io.BufferedReader; impor ...
- Socket通讯-C#客户端与Java服务端通讯(发送消息和文件)
设计思路 使用websocket通信,客户端采用C#开发界面,服务端使用Java开发,最终实现Java服务端向C#客户端发送消息和文件,C#客户端实现语音广播的功能. Java服务端设计 packag ...
- Spring Boot+Socket实现与html页面的长连接,客户端给服务器端发消息,服务器给客户端轮询发送消息,附案例源码
功能介绍 客户端给所有在线用户发送消息 客户端给指定在线用户发送消息 服务器给客户端发送消息(轮询方式) 项目搭建 项目结构图 pom.xml <?xml version="1.0&q ...
- server-sent-event使用流信息向客户端发送数据
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- socket 服务器向指定的客户端发消息
一.需求 需求如题. 当多个客户端连接服务器时,服务器如何给指定的客户端发送消息. 二.解决方案 核心思想: 在服务器端,需保存不同客户端的socket列表及客户端相关信息. socket含有发送方和 ...
- zookeeper源码分析之三客户端发送请求流程
znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...
- RTMPdump(libRTMP) 源代码分析 8: 发送消息(Message)
===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...
随机推荐
- C++字符转换等常用方法
下面是C++中字符编码格式转换(包括ANSI转换为UTF8,UTF8转换为ANSI,ANSI转换为unicode,unicode转换为ANSI,UTF8转换为unicode,unicode转换为UTF ...
- PostgreSQL查看表、表索引、视图、表结构以及参数设置
-- 表索引select * from pg_indexes where tablename='person_wechat_label';select * from pg_statio_all_ind ...
- springboot自动配置国际化失效分析
最近在整理springBoot国际化时,发现国际化没有生效,通过报错提示在 MessageTag -> doEndTag处打断点 最后发现messageSource并不是ResourceBund ...
- JDBC 24homework
编写程序: 1. 创建商品信息表Goods(包含编号Code.名称Name.数量Number.单价Price) 2. 设计程序流程,由用户选择:插入.删除.修改.查询 程序效果如下: (1)根据提示输 ...
- layer的两种提示信息
layer.msg('您的航班价格已变动,请返回重新选择航班!', { time: 10000, shade : [0.6 , '#000' , true], btn: ['返回列表', '关闭'], ...
- LCN分布式事务管理(一)
前言 好久没写东西了,9月份换了份工作,一上来就忙的要死.根本没时间学东西,好在新公司的新项目里面遇到了之前没遇到过的难题.那遇到难题就要想办法解决咯,一个请求,调用两个服务,同时操作更新两个数据库. ...
- jQuery快速入门专题
jQuery入门专题 本人博客特点:最高重要等级为*****(五红星),依次减少代表重要性相对较低! 一.jQuery简介 jQuery 是一个 JavaScript的一个库,也就是说jQuery是基 ...
- 关于学习电信nb-iot的小结
关于这几天对nb-iot的学习的总结和遇到的坑 初步学习nb-iot,了解到了nb-iot对于传感器数据传输功能的强大: 废话不多说,对于nb-iot我们选择的有人的模块,选择B5频段也就是电信的nb ...
- vue学习(3)-增删改查
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- yocto project user’s guide
http://www.yoctoproject.org/docs/2.1/ref-manual/ref-manual.html 参考手册 http://www.yoctoproject.org/doc ...