- 浏览: 14421 次
最新评论
TCP长连接服务的Java实现
- 博客分类:
- 技术杂绘
TCP长连接服务的Java实现
2010年06月29日
梁应宏 TCP长连接服务在传统的智能网应用中扮演着重要的角色。由于其传输的高效率,在智能网SCP和IP的各个模块之间,大量使用了这种服务。例如,SS7gateway与SCF、SCF与INES、INES与外部节点、CN与VN,等等。
相反,在各种WEB应用中,广泛使用TCP短连接服务。基于HTTP承载的各种应用协议,如HTML,XML,SOAP等,多数使用TCP短连接服务。原因有二:一是这些HTTP协议的数据包较大,传输所占的开销较大,连接建立的开销相对较小。此时使用长连接对性能的提升并不明显。二是相对于长连接而言,无论是对于客户端还是服务端,短连接的实现难度要低很多。
以网通水平业务平台SPGW为例,多数对外接口采用HTTP/XML/SOAP协议和短连接。然而,出于性能的考虑,还有两个接口采用TCP长连接:
l 东向接口(SPGW-DSMP):SCCP协议使用二进制消息。
l 南向接口(SPGW-SMSC):SPGW与短消息网关SMSC之间的SM7协议使用二进制消息。
从Java开发语言的角度,短连接的使用比较简单。因为Java的IO库已经提供了一个httpConnection类,成熟可靠,使用方便。但是,对于TCP长连接的使用,Java的IO库并没有直接的支持。本文将探讨对TCP长连接服务的一般需求和我们的实现考虑。
以下也简称TCP长连接服务为TCP服务。 具有网络编程经验的人都知道,TCP程序的编写是"易学难精"。很容易编写一个TCP程序,具有一定的功能并且在少数正常情况下可以运行。但是,要想让它在各种网络条件、各种负荷情况下都能稳定运行,却不是一件简单的工作。具体说来,TCP长连接服务需要满足以下条件: 实现这一点的关键是,消息的接收操作必须是异步的。以SPGW与短信网关之间的消息流程为例,如下:
如上图所示,SPGW可以不等待上一消息的应答消息,就发送下一个短消息。因此在同一个TCP connection上,SM7消息的接收必须是异步的,否则就会阻塞后续消息的发送。 健壮性要求,TCP长连接服务不仅要能适应良好的网络情况和低负荷,而且要能适应差的网络情况和高负荷。实现这一点需要做到:
l 应用级心跳:自动检测网络故障。
l 应用级重连:自动排除网络故障。
l 请求分发:需要将请求消息分发到消息队列或者独立的线程中,以免阻塞接收线程的工作。
l 统计与管理:可以查询统计TCP服务模块的工作情况。也可以通过某种标准的网络管理协议(如SNMP)来控制TCP连接的状态,如打开或者关闭。 l 同步的响应消息接收API:尽管TCP服务内部对消息的接收是异步的,但是,它需要向应用模块提供同步的响应消息接收API,以简化应用模块的开发。
l 明确的双向接口:一般的服务包只需要提供单向的API接口,由应用模块调用。但是,TCP服务包不同,除了被应用模块调用外,它还要对应用模块进行回调。例如,当接收到消息时,需要回调应用模块的方法,对消息进行定界和分发;在进行心跳检测时,需要回调应用模块的编解码方法。因此需要明确地定义TCP模块和应用模块之间的双向接口。 在我们以往的程序实现中,一般都是采用单connection,异步消息收发的方式。
Extra Node
Application
Main Thread
Transceive Thread
Message Queue
整个系统的逻辑结构大致如上。Application一般分为Main和Transceive两个线程。前者用于完成应用的逻辑,后者完成消息的收发。它们之间通过一个共享的Message Queue来通信。二者的流程使用伪码表示如下:
Main Thread:
while(true)
从Message Queue取输入消息
处理该消息(中间可能产生输出消息送到Message Queue)
Transceive Thread:
while(true)
if(connection 可写)
从Message Queue取输出消息
将该消息写到connection
if(connection 可读)
从connection读输入消息
将该消息送到Message Queue
对于Transceive Thread,判断connection是否可读写,是为了避免阻塞。这是通过socket API的select函数来完成的。
在实际的实现中,由于C语言的多线程存在移植性问题,以上两个线程一般合为一个:
Single Thread:
while(true)
执行Main Thread的操作
执行Transceive Thread的操作 以上方式的优点是非常高效,这已经为我们以前的系统的性能表现所证明。 以上方式的缺点是Main Thread的编写比较麻烦。因为没有同步的消息接收API,我们需要使用FSM之类的机制将多条消息关联起来。当一条消息发出后,需要设置FSM实例的状态和定时器。当回应消息收到时,将回应消息投递到对应的FSM实例进行处理。使用FSM机制进行开发,对于一般的应用还是太复杂了。
另外,传统的实现方式不好解决TCP服务回调应用协议功能的问题,往往将应用协议的部分功能(如定界、编解码)集成到TCP模块中。结果随着应用协议类型的增加,TCP模块变得越来越臃肿和复杂。出现这一问题的部分原因是,与Java不同,C/C++语言不存在Interface(接口)这种语言结构,用于明确地定义两个模块之间的调用接口。 TCP服务模块的功能是:
1. 消息收发功能:提供了两个发送API:
l sendRecv:发送请求消息,并且同步等待响应消息。
l send:发送消息并立即返回。用于无需等待响应消息的场合。
2. 心跳功能:通过设置心跳属性,如心跳模式、心跳间隔、是否发送心跳、是否显示心跳等,TCP模块自动执行心跳检查。
3. 重连功能:对于Tcp客户端,通过设置重连属性,如是否重连,重连间隔等,TCP模块在连接断开后,自动执行重连操作。
4. 请求分发:将请求消息分发到独立的线程中,并自动调用应用模块的方法进行处理。
5. 统计与管理:可以查询统计TCP服务模块的工作情况。也可以通过SNMP和JMX来控制TCP连接的状态,如打开或者关闭。 为了同时满足高性能和易用性的要求,TCP模块充分利用了Java语言对多线程的良好支持。它包括如下线程:
HeartBeat thread
S
Tcp服务模块
S
Recv thread
S
App 模块
S
Send thread1
S
Send thread2
S
Process request thread3
S
sendRecv
S
send
S
processRequest
S
Reconnect thread
S
Send thread:发送线程就是应用线程。也就是说,TCP模块在应用线程中完成发送操作。
Recv thread:每个TCP连接都启动一个接收线程,用于接收来自对端的消息。
HeartBeat thread:每个TCP连接都启动心跳线程,用于定期向对端发送心跳消息,并检查是否及时收到响应。
Reconnect thread:TCP模块启动一个重连线程,用于定期检查连接的状态,并试图重连关闭的连接。
Process request thread:请求处理线程是应用线程。当接收线程收到一个请求消息,它会启动一个请求处理线程,将请求消息投递给该线程进行处理。 下面简要列出TCP模块提供给应用模块的ITcpService接口:
l 打开连接:open(String ip, int port)
l 关闭连接:void close()
l 发送数据包:void send(byte[] data)
l 发送数据包并等待响应:byte[] sendRecv(byte[] data, int timeout)
l 设置应用协议接口:void setTcpMessage(ITcpMessage tcpMessage);
下面简要列出应用模块提供给TCP模块的ITcpMessage接口:
l 判断给定的消息是否为请求消息:boolean isRequest(byte[] data)
l 判断给定的消息是否为心跳消息:boolean isHeartBeat(byte[] data)
l 判断给定的消息是否有效:boolean isValid(byte[] data)
l 取消息的长度:int getLength(byte[] data)
l 获取给定的消息的Key(消息的Key用于关联一对请求和响应):String getKey(byte[] data)
l 编码心跳请求消息:byte[] EncodeHeartBeatRequest()
l 编码心跳响应消息:byte[] EncodeHeartBeatResponse(byte[] request);
l 处理请求消息:void processRequest(byte[] data);
l 执行重连操作:void reconnect();
l 设置TcpService对象:void setTcpService(ITcpService tcpService); 在ITcpMessage接口中,需要关注的是getLength方法:
int getLength(byte[] data) throws TcpServiceException
这一方法非常关键。表面上的功能是取消息的长度,实际上TCP模块使用此方法对消息进行定界(delimiter)。
所有使用TCP长连接服务的应用协议都需要考虑如何对消息进行定界的问题。这是因为TCP连接上每次收到的数据包不一定正好对应一条完整的消息,可能需要对数据包进行拆分/合并操作。这个处理过程烦琐而容易出错,主要由TCP模块完成。但是应用模块需要实现合适的getLength方法,才能得到健壮的定界结果。
参数
data
发表评论
-
Java 开发员AJAX 常见问题
2012-01-20 00:49 600Java 开发员AJAX 常见问题 ... -
Android IPC进程间通讯机制
2012-01-20 00:49 747Android IPC进程间通讯机制 2011年06月17日 ... -
说说IO - IO的分层
2012-01-20 00:49 672说说IO - IO的分层 2011年 ... -
使用 libevent 和 libev 提高网络应用性能
2012-01-20 00:49 1449使用 libevent 和 libev 提 ... -
i9001
2012-01-17 01:07 753i9001 2011年12月13日 一、外观检查,外壳、 ... -
从零开始- Android刷机指南<一>
2012-01-17 01:07 749从零开始- Android刷机指南 2011年07月27日 ... -
接上一篇
2012-01-17 01:07 653接上一篇 2011年04月25日 第 2 章 路由器基 ... -
ARM经典100问
2012-01-17 01:07 807ARM经典100问 2011年03月13日 第1章 体系 ... -
libevent源码深度剖析四
2012-01-15 19:48 630libevent源码深度剖析四 ... -
valgrind结果查看
2012-01-15 19:48 519valgrind结果查看 2011年12月30日 程序示 ... -
给大学生学习ARM和FPGA的建议(转)
2012-01-15 19:48 614给大学生学习ARM和FPGA的 ... -
教大家S40/java所有玩机技巧,喜欢请转载 加QQ657752021学习更多技术
2012-01-15 19:48 856教大家S40/java所有玩机 ... -
【下一页】linux(部分)
2012-01-15 19:48 713【下一页】linux(部分) 2012年01月09日 一 ... -
第2章 saas 成熟度模型-悟空悟道-iteye技术网站
2012-01-11 12:02 535第2章 saas 成熟度模型-悟空悟道-iteye技术网站 ... -
使用 JavaScript 获取 table 行号和列号
2012-01-11 12:02 796使用 JavaScript 获取 table 行号和列号 2 ... -
Extending Ext
2012-01-11 12:02 601Extending Ext 2011年07月01日 fr ... -
ReflectionUtils
2012-01-11 12:02 677ReflectionUtils 2011年07月01日 ... -
JPA中复合主键的映射
2012-01-11 12:01 548JPA中复合主键的映射 2011年07月01日 在航空系 ...
相关推荐
java建立TCP长链接,再加上心跳机制,下载后可以直接运行,包括server和client
modbusTCP协议java实现(带注释)
java多线程实现TCP连接UDP聊天的聊天程序
基于java的modbus TCP通信
手写简化版tcp长链接的socket实现,主要功能有断开重连,以及收发读取解码解析,适用于需要用到长链接的原生开发。
java socket长连接客户端服务端(标准实例),准确无误,流行结构。
多线程TCP连接,用java实现 多线程TCP连接,用java实现
JAVA采用Netty库实现基于以DTU传输的TCP服务器 ,可以支持多端口通讯 ,同时也支持 多协议解析
基于Apache Mina实现的TCP长连接和短连接实例 详细说明,可参见blog http://blog.csdn.net/peterwanghao/article/details/6900523
java下用多线程实现tcp网络连接,分别放在两个java 文件中
JAVA实现长连接(含心跳检测)Demo,基于https://blog.csdn.net/zmx729618/article/details/54378259该作者的博文实现的代码示例。
使用纯JAVA实现的UDP和TCP连接 大家使用JAVA做项目的时候,较多使用NETTY等框架。 但是了解下最基本的原始用法对初学者是非常适用的
java 实现TCP 服务端程序 带用户登录检测带 数据库 来自网络
运用JAVA的concurrent.ExecutorService线程池实现socket的TCP和UDP连接
1、本项目则是使用Java实现TCP的Socket网络通信,包含C/S软件架构的程序设计,偏向实践,更加有趣! 2、实现简单有趣的“创意”聊天机器人。 3、建立通信规则: Server和Client之间需要约定相同的规则,保证正常通信...
自己写的Java的socket长连接实例 已测试
可运行的java项目,分别用两种方案实现modbus协议读写1、jlibmodbus 2、modbus4j 有问题可联系QQ:361440206
2) 基于Java Socket TCP和UDP实现一个简易的网络文件服务程序,包含服务器端FileServer和客户端FileClient; 3) 服务器端启动时需传递root目录参数,并校验该目录是否有效; 4) 服务器启动后,开启TCP:2021端口,...
要求客户机可以向服务器传送数据,服务器从客户端中读取数据。实际上,服务器也可以向客户机发送数据,只要像客户机那样建立输出流即可;客户机也可以接受服务器的数据,只要像服务器那样建立连接即可。
西门子s7-200 Smart plc modbus tcp 通讯代码(Java版), 是本人...具体操作是以PLC为modbus TCP 主站,Java语言开发出从站,与PLC 主站进行连接通讯,然后调用不同的函数获取输入输出状态的值,寄存器的值。2019.3.7