Mina 怎么为这样的协议写url编码解码器器

使用mina2通信的完整代码朋友们可以去http://download.csdn.net/detail/u56861下载
下面只对编解码协议进行解释
package lhy.
import java.nio.ByteO
import java.nio.charset.C
import lhy.client_domain.MsgP
import org.apache.mina.core.buffer.IoB
import org.apache.mina.core.session.IoS
import org.apache.mina.filter.codec.CumulativeProtocolD
import org.apache.mina.filter.codec.ProtocolDecoderO
&* @see 协议解码
&* @author lhy
public class MsgProtocolDecoder extends CumulativeProtocolDecoder &{ &
& & private Charset charset= &
& & public MsgProtocolDecoder() { &
& & & & this(Charset.defaultCharset()); &
& & public MsgProtocolDecoder(Charset charset) { &
& & & & this.charset = &
public void dispose(IoSession arg0) throws Exception {
protected boolean doDecode(IoSession session, IoBuffer ioBuffer, ProtocolDecoderOutput out) throws Exception {
ioBuffer.order(ByteOrder.LITTLE_ENDIAN);&
MsgPack mp = (MsgPack) session.getAttribute(&nac-msg-pack&); // 从session对象中获取“xhs-upload”属性值&
if(null==mp){
if (ioBuffer.remaining() &= 8) {
//取消息体长度
int msgLength = ioBuffer.getInt();&
int msgMethod = ioBuffer.getInt();
mp=new MsgPack();
mp.setMsgLength(msgLength);
mp.setMsgCode(msgMethod);
session.setAttribute(&nac-msg-pack&,mp);
//如果tcp缓冲区数据大于上次保存的的,解析消息体
if(ioBuffer.remaining()&=mp.getMsgLength()){
byte [] msgPack=new byte[mp.getMsgLength()];
ioBuffer.get(msgPack);
mp.setMsgPack(new String(msgPack,charset));
session.removeAttribute(&nac-msg-pack&);
out.write(mp);
package lhy.
import java.io.NotSerializableE
import java.io.S
import java.nio.ByteO
import java.nio.charset.C
import lhy.client_domain.MsgP
import org.apache.mina.core.buffer.IoB
import org.apache.mina.core.session.IoS
import org.apache.mina.filter.codec.ProtocolDecoderO
import org.apache.mina.filter.codec.ProtocolEncoderA
import org.apache.mina.filter.codec.ProtocolEncoderO
&* 自定义编码
&* @author Administrator
public class MsgProtocolEncoder extends ProtocolEncoderAdapter{
private Charset charset=
& & public MsgProtocolEncoder(Charset charset) {
& & & & this.charset = & &&
& & } & &&
& & //在此处实现对MsgProtocolEncoder包的编码工作,并把它写入输出流中 & &&
& & public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {&
& & & & if(message instanceof MsgPack){
& & & & MsgPack mp = (MsgPack)&
& & & & //第一个参数指定初始化容量,第
& & & & //二个参数指定使用直接缓冲区还是JAVA 内存堆的缓存区,默认为false。
& & & & IoBuffer buf = IoBuffer.allocate(mp.getMsgLength());
& & & & buf.order(ByteOrder.LITTLE_ENDIAN);
& & & & //这个方法设置IoBuffer 为自动扩展容量,也就是前面所说的长度可变,
& & & & & & &buf.setAutoExpand(true); & &
& & & & & & &//设置消息内容的长度
& & & & & & &buf.putInt(mp.getMsgLength());&
& & & & & & &//设置消息的功能函数
& & & & & & &buf.putInt(mp.getMsgCode());
& & & & & & &if (null != mp.getMsgPack()) {
& & & & & & buf.put(mp.getMsgPack().getBytes(charset));
& & & & & & &} &&
& & & & & & &buf.flip(); & &&
& & & & & & &out.write(buf); &
& & & & & & &out.flush();
& & & & & & &buf.free();
& & } & &&
& & public void dispose() throws Exception { & &&
本文已收录于以下专栏:
相关文章推荐
mina自带了心跳包机制,我是每隔15秒发送一次心跳包,若30秒内没有收到,则认为超时。
网络连接的主题函数是:
* 30秒后超时
private st...
转自:http://my.oschina.net/yjwxh/blog/174633
摘要 心跳协议,对基于CS模式的系统开发来说是一种比较常见与有效的连接检测方式,最近在用MINA框架,原本自...
上一节实现了简单的EchoServer,客户端说什么,服务器返回you say:什么,但它是怎么传输的呢,协议是什么格式的呢,业务逻辑和协议真的分得很开呢
它的编码解...
前阵子用libjpeg-turbo实现jpeg图像在内存中编码与解码
参见《libjpeg:实现jpeg内存解压缩塈转换色彩空间/压缩分辨率》,《libjpeg:实现jpeg内存压缩暨error_...
我们都知道MINA中是使用责任链的方式来实现将二进制字节流数据转换为java对象,或者将java对象转换为二进制字节流数据的,那么这个转换过程到底是怎么进行的呢?这就涉及到MINA中的编码与解码问题了...
mina支持自定义编码和解码,
他的最新文章
讲师:汪剑
讲师:刘道宽
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)&>&mina仿qq聊天功能,自定义协议,协议的编码和解码详解,发送xml对象json,mina开发大全,详细api,mina心跳
mina仿qq聊天功能,自定义协议,协议的编码和解码详解,发送xml对象json,mina开发大全,详细api,mina心跳
上传大小:5.74MB
mina仿qq聊天功能,自定义协议,协议的编码和解码详解,发送xml对象json,mina开发大全,详细api
mina聊天 mina解码编码 mina协议开发 mina仿qq mina消息xml
mina开发的在线聊天工具,mina仿qq功能,mina自定义协议,可以仿http请求,mina心跳等技术大全,mina功能大揭密
综合评分:4.3(135位用户评分)
所需积分/C币:
下载个数:1395
{%username%}回复{%com_username%}{%time%}\
/*点击出现回复框*/
$(".respond_btn").on("click", function (e) {
$(this).parents(".rightLi").children(".respond_box").show();
e.stopPropagation();
$(".cancel_res").on("click", function (e) {
$(this).parents(".res_b").siblings(".res_area").val("");
$(this).parents(".respond_box").hide();
e.stopPropagation();
/*删除评论*/
$(".del_comment_c").on("click", function (e) {
var id = $(e.target).attr("id");
$.getJSON('/index.php/comment/do_invalid/' + id,
function (data) {
if (data.succ == 1) {
$(e.target).parents(".conLi").remove();
alert(data.msg);
$(".res_btn").click(function (e) {
var q = $("#form1").serializeArray();
console.log(q);
var res_area_r = $.trim($(".res_area_r").val());
if (res_area_r == '') {
$(".res_text").css({color: "red"});
$.post("/index.php/comment/do_comment_reply/", q,
function (data) {
if (data.succ == 1) {
var $target,
evt = e || window.
$target = $(evt.target || evt.srcElement);
var $dd = $target.parents('dd');
var $wrapReply = $dd.find('.respond_box');
console.log($wrapReply);
var mess = $(".res_area_r").val();
var str = str.replace(/{%header%}/g, data.header)
.replace(/{%href%}/g, 'http://' + window.location.host + '/user/' + data.username)
.replace(/{%username%}/g, data.username)
.replace(/{%com_username%}/g, _username)
.replace(/{%time%}/g, data.time)
.replace(/{%id%}/g, data.id)
.replace(/{%mess%}/g, mess);
$dd.after(str);
$(".respond_box").hide();
$(".res_area_r").val("");
$(".res_area").val("");
$wrapReply.hide();
alert(data.msg);
}, "json");
/*删除回复*/
$(".rightLi").on("click",'.del_comment_r', function (e) {
var id = $(e.target).attr("id");
$.getJSON('/index.php/comment/do_comment_del/' + id,
function (data) {
if (data.succ == 1) {
$(e.target).parent().parent().parent().parent().parent().remove();
$(e.target).parents('.res_list').remove()
alert(data.msg);
//填充回复
function KeyP(v) {
$(".res_area_r").val($.trim($(".res_area").val()));
评论共有68条
不错,学习了
跑起来报错,课能哪里有问题
不错,对学习有帮助
不错,是我需要的 有参考价值
很不错的一个东西 正在研究
感谢分享啊
跟qq没什么关系,但是当作mina入门的学习还是很不错的
mina使用示例
不是我要的东西.
就是入门教程,跟QQ没关系
审核通过送C币
java自主学习知识总结
创建者:qq_
iText 7 相关jar包
创建者:ealu1234
39本架构类书籍
创建者:jsntghf
上传者其他资源上传者专辑
Shiro入门教程
HP-Socket下载
artEditor下载artEditor.min.js下载
gif动画录制工具
HTML5全兼容九宫格布局
开发技术热门标签
VIP会员动态
下载频道积分规则调整V1710.18
CSDN下载频道积分调整公告V1710.17
开通VIP,海量IT资源任性下载
spring mvc+mybatis+mysql+maven+bootstrap 整合实现增删查改简单实例.zip
CSDN&VIP年卡&4000万程序员的必选
为了良好体验,不建议使用迅雷下载
mina仿qq聊天功能,自定义协议,协议的编码和解码详解,发送xml对象json,mina开发大全,详细api,mina心跳
会员到期时间:
剩余下载个数:
剩余C币:593
剩余积分:786
为了良好体验,不建议使用迅雷下载
积分不足!
资源所需积分/C币
当前拥有积分
您可以选择
程序员的必选
绿色安全资源
资源所需积分/C币
当前拥有积分
当前拥有C币
(仅够下载10个资源)
全站1200个资源免积分下载
为了良好体验,不建议使用迅雷下载
资源所需积分/C币
当前拥有积分
当前拥有C币
全站1200个资源免积分下载
资源所需积分/C币
当前拥有积分
当前拥有C币
您的积分不足,将扣除 10 C币
全站1200个资源免积分下载
为了良好体验,不建议使用迅雷下载
你当前的下载分为234。
你还不是VIP会员
开通VIP会员权限,免积分下载
你下载资源过于频繁,请输入验证码
你下载资源过于频繁,请输入验证码
您因违反CSDN下载频道规则而被锁定帐户,如有疑问,请联络:!
若举报审核通过,可奖励20下载分
被举报人:
举报的资源分:
请选择类型
资源无法下载
资源无法使用
标题与实际内容不符
含有危害国家安全内容
含有反动色情等内容
含广告内容
版权问题,侵犯个人或公司的版权
*详细原因:
mina仿qq聊天功能,自定义协议,协议的编码和解码详解,发送xml对象json,mina开发大全,详细api,mina心跳博客分类:
mina允许自定义数据传输的编码和解码方式
需要
实现ProtocolCodecFactory接口的工厂类
实现ProtocolDecoder接口的解码类
实现ProtocolEncoder接口的编码类
本例以client和server端都是java实现
首先定义传输的数据格式:
编码和解码都是针对数据字节。
数据格式:A+B+C+D
A:固定长度 6个字节,用来简单表示时间戳,
& 月日年,时分秒每个一字节,年取后两位
B:内容(C+D)的length,固定长度,4个字节
C:请求指令:固定长度,4个字节
D:传递内容:不固定,json格式
这样针对上面的数据结构定义一个bean
public class MyMessage implements Serializable {
private static final long serialVersionUID = 7872279L;
private D//时间
private byte[]//内容
public int getCommand() {
public void setCommand(int command) {
public byte[] getContents() {
public void setContents(byte[] contents) {
this.contents =
public int length(){
return contents.
public Date getDate() {
public void setDate(Date date) {
this.date =
编码类,继承org.apache.mina.filter.codec.CumulativeProtocolDecoder
重新encode方法即可
public class MyEncoder implements ProtocolEncoder {
public void encode(IoSession session, Object message,
ProtocolEncoderOutput out) throws Exception {
MyMessage msg = (MyMessage)
IoBuffer buffer = IoBuffer.allocate(1024);
buffer.setAutoExpand(true);
//编码数据结构的A,时间戳
buffer.put(getTimeTag(msg.getDate()));
//数据结构的B
buffer.putInt(msg.length()+4);
//数据结构的C
buffer.putInt(msg.getCommand());
//数据结构的D
buffer.put(msg.getContents());
buffer.flip();
out.write(buffer);
public void dispose(IoSession session) throws Exception {
public static byte[] getTimeTag(Date date){
if(date == null){
date = new Date();
Calendar c = Calendar.getInstance();
c.get(Calendar.YEAR);
String dateStr = sdf.format(date);
String[] dates = dateStr.split("-");
byte[] bt = new byte[dates.length];
for(int a=0;a&dates.a++){
bt[a] = Byte.parseByte(dates[a]);
解码类,继承自org.apache.mina.filter.codec.CumulativeProtocolDecoder
重新decode方法
public class MyDecoder extends CumulativeProtocolDecoder {
protected boolean doDecode(IoSession session, IoBuffer in,
ProtocolDecoderOutput out) throws Exception {
//读取数据结构A
byte[] dateTag = new byte[6];
in.get(dateTag);
//读取数据结构B
int length =in.getInt();
//读取数据结构C
int command = in.getInt();
//读取数据结构D
byte[] bytes = new byte[length-4];
in.get(bytes);
//提取出数据结构对象
MyMessage msg = new MyMessage();
msg.setCommand(command);
msg.setContents(bytes);
out.write(msg);
工厂类,很简单。主要是给server端和client端提供使用
public class MyCodeFactory implements ProtocolCodecFactory{
private ProtocolD
private ProtocolE
public MyCodeFactory() {
decoder = new MyDecoder();
encoder = new MyEncoder();
public ProtocolEncoder getEncoder(IoSession session) throws Exception {
public ProtocolDecoder getDecoder(IoSession session) throws Exception {
写到这里基本上自定义编码解码部分就完成了,下面使用就和其他已有mina提供的
编码解码filter一样使用了
首先还是要定义两个handler
server端handler:
public class MyServerHandler extends IoHandlerAdapter {
public void exceptionCaught( IoSession session, Throwable cause ) throws Exception
cause.printStackTrace();
session.close(true);
public void messageReceived( IoSession session, Object message ) throws Exception
//收到了上面解码后的消息
MyMessage msg = (MyMessage)
if(message == null){
int cmd = msg.getCommand();
String body = new String(msg.getContents());
String result = "";
根据请求指令的不同,调用后续的业务,
然后响应内容到client
System.out.println(msg.getCommand()+"-----"+new String(msg.getContents()));
session.write(msg);
client端handler
public class MyClientHandler extends IoHandlerAdapter {
public void sessionOpened(IoSession session) throws Exception {
//session.write(obj);
public void messageReceived(IoSession session, Object message)
throws Exception {
//收到消息,。。。
MyMessage gm = (MyMessage)
System.out.println(gm.getCommand()+":"+new String(gm.getContents()));
public void exceptionCaught(IoSession session, Throwable cause)
throws Exception {
session.close(true);
server端配置:
IoAcceptor accepter = new NioSocketAcceptor();
ProtocolCodecFilter coderFilter =
//使用自定义的编码解码filter
new ProtocolCodecFilter(new MyCodeFactory());
accepter.getFilterChain().addLast("a", new LoggingFilter());
accepter.getFilterChain().addLast("b",coderFilter);
//绑定handler
accepter.setHandler(new MyServerHandler());
accepter.getSessionConfig().setReadBufferSize(2048);
accepter.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
accepter.bind(new InetSocketAddress(8484));
client端配置:
NioSocketConnector connector = new NioSocketConnector();
connector.setConnectTimeoutMillis(20000);
connector.getFilterChain().addLast("codes", new ProtocolCodecFilter(
new MyCodeFactory()));
connector.setHandler(new MyClientHandler());
ConnectFuture future = connector.connect(new InetSocketAddress("localhost", 8484));
future.awaitUninterruptibly();
IoSession session =
session = future.getSession();
MyMessage gm = new MyMessage();
Map&String, String& map = new HashMap&String, String&();
gm.setCommand(101);
map.put("name", "bird");
map.put("age", "7");
//JsonUtil json工具类,map转json,随便找个就行
gm.setContents(JsonUtil.objectToStr(map).getBytes());
session.write(gm);
connector.dispose();
运行后server端会打印:1001-----{"name":"c","age":"7"}
client:1001:{"name":"c","age":"7"}
都能正确获取到传递的内容。
浏览: 74643 次
来自: 北京
这个还真没注意,8错
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'博客访问: 37035
博文数量: 13
博客积分: 414
博客等级: 一等列兵
技术积分: 152
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: Java
&& & 使用Mina过程中的随笔翻译,发上来给用得到的朋友。由于英语水平有限,难免有错误,全当参考的参考了……另外贴上来的图片全都挂了,后边给出了word文档,可以下载。
&&&&第一章——开始
& & 在这一章中,我们将给你什么是MINA、NIO的第一感觉,以及为什么我们在NIO上构建框架。我们还展示了如何运行一个基于MINA的非常简单的程序例子。
1、NIO综述
& & NIO APIs在Java 1.4中被给出,如今它已经在在大量的程序中被使用了。NIO APIs允许非阻塞IO操作。
& & 注意:首先,知道MINA是在NIO 1的基础上开发的是很重要的,如今在Java 7中一个新版本的NIO-2已经被设计出来,但是我们还没有从这个版本所带有的特性中获益。
& & 注意:知道N在NIO中的意思是New也是重要的,但是我们将在很多地方使用非阻塞(Non-Blocking)这个术语。NIO-2需要被看为New New I/O……
& & java.nio.*包包含以下关键结构
Buffers——数据容器
Chartsets——bytes和Unicode之间的翻译容器
Channels——代表I/O操作的连接实体
Selectors——提供可选择的多路复用非阻塞IO
Regexps——提供一些操作正则表达式的工具
& & 我们将集中关注MINA框架的Channels、Selectors以及Buffers部分,其它部分我们将对用户进行隐藏。
& & 本手册将集中关注在这些内部组件上构建我们所需要的系统。
NIO vs BIO
& & 知道BIO和Clocking IO这两种APIs之间的不同是很重要的,在阻塞模型中依赖普通的sockets链接:当你读、写或者在socket上做任何操作的时候,被调用的操作将会阻塞调用者,知道操作完成。
& & 在一些例子中,关键是能够调用操作,并期望被调用的操作在操作完成之后能够通知调用者:这使得调用者在平均的运行时间里可以做更多的事情。
& & 这也就是NIO的优点,当你有很多的socket链接时,NIO能帮你更好的处理它们:你不需要为每一个链接创建指定的进程,你只需要使用少量的进程来做同样的事情。
& & 如果你想获得有关于NIO的更多信息,网上有很多不错的文章,还有几本书讲述了有关NIO的问题。
2、为什么开发MINA
& & 编写网络程序被看做是繁重的底层开发。它一个开发者不经常学习和知晓的区域,要么是因为很久之前在学校里学过但是现在已经忘了,或者是因为复杂的网络层总是被高层隐藏,总之你没有深入学习过它。
& & 另外当涉及到异步IO的时候,一个额外的发杂层又参杂了进来:time。
& & BIO(Blocking IO)和NIO(Non-Blocking
IO)之间的最大的区别是,在BIO中,你发送一个请求,然后需要一直等待直到你获得答复。在服务器端,这意味着一个进程将会与一个对内连接相联系,所以你不得不处理复杂的多路链接。在NIO中,另一方面,你需要解决非阻塞系统的同步工作,这就意味当一些事情发生之后,你的程序将被唤醒。在NIO中,你不需要调用和等待结果,你发送完命令之后,之后当恢复准备好了你将获得通知。
框架的需求
& & 考虑到这些不同,事实上许多的程序都期待在调用网络层的时候有一个阻塞模式,而最好的解决方法就是编写一个模拟阻塞的框架来隐藏这些不同。这就是为什么需要MINA。
& & 但是MINA可以做更多。它为使用TCP、UDP和其他机制进行通信提供统一的接口。当你只考虑TCP和UDP的时候,一个是有连接的(TCP),而另外一个是无连接的(UDP)。MINA掩饰这些不同,使你能够专注于你的程序的两个重要部分:程序的编码以及协议的编码和解码。
& & MINA不但可以处理TCP和UDP,它还通过VmpPipe或APR为串口通信(RSC232)提供了一个高层接口。
& & 最后但不是最终的,MINA被特别的设计为能够在客户端和服务器端都能运行的网络框架。
& & 编写一个关键是能够扩展的系统,同时在性能和内存使用方面能够符合服务器的要求:这就是MINA所擅长的,它使得构建你自己的服务器变得简单。
什么时候使用MINA?
& & 这是一个有趣的问题!MINA在一些情况下并不是最好的选择。当你想要使用MINA的时候有几个元素需要考虑。让我们来看看:
当你没有特定的性能环境的时候它是易用的,这个时候MINA是一个很好的选择因为它让你更简单的构建一个服务器或客户端,而不用在BIO和NIO的上层重复编写处理代码和处理各种参数。你甚至能够用简单地10行代码完成你的服务器,而且可以避免更少的问题。
一定数量的用户连接BIO是比NIO快的。不同的是大约30%支持BIO。这对于拥有几千条用户连接的系统是正确的,但是在一定程度上,BIO已经停止扩展了:你不能用一个用户一个进程的方式处理百万级的用户连接!而NIO可以。另一方面你在MINA部分所花费的时间在你整个程序的编码过程中也许并不重要。在一定程度上,编写一个更快的网络层也许并没有什么价值,因为性能基本得不到提升。
在全世界有数万的程序在使用MINA,这可以证明MINA很有用。有很多的Apache项目基于MINA,它们工作的都很好。这也保证了你不用花费数小时的时间来解决你自己的网络层实现中所遇到的奇怪问题了。
MINA当前支持的已存在协议有:HTTP、XML、TCP、LDAP、DHCP、NTP、DNS、XMPP、SSH、FTP……在一定程度上,MINA不只是一个NIO的网络框架,它还是一个带有许多协议实现的网络框架。MINA的一个未来发展是成为一个你能够使用的各种存在协议的集合。
& & MINA是一个简单地同时也是全功能的网络程序开发框架,它提供了以下功能:
为各种传输方式提供了同一个API:
基于NIO的TCP/IP & UDP/IP
基于RXTX的串口通信(RS232)
In-VM pipe 通信
你可以在它上边实现你自己的通信!
作为扩展的过滤器接口;类似于Servlet的过滤器
底层和高层API:
底层:使用ByteBuffers
高层:使用用户定义的信息类和编码
可定制的进程模型:
多个线程池(SEDA)
Out-of-the-box
SSL.TLS.StartTLS 支持使用Java 5
过载保护和流量节流
模拟对象的单元测试
JMX可管理性
基于流的IO操作:StreamIoHandler
与一些知名的容器集成,例如PicoContainer和Spring
从Netty的平滑迁移,MINA的祖先
& & 我们将运行一个MINA包提供的示例代码为你展示如何简单地使用MINA。
& & 你所要做的第一件事就是在你的程序中使用MINA之前配置你的环境。我们将描述你需要安装什么和如何运行MINA程序。没什么技巧,只是一个MINA的测试……
& & 首先你需要从现在站点现在最新的MINA发布包。最好下载最新版,除非你有很好的理由不这样做……
& & 一般而言,如果你想使用MINA构建你自己的项目,你不需要下载什么东西,你只需要将一个MINA库包含到你的项目中就可以了:你只需要告诉你的IDE你要使用MINA的jar包就可以了。
MINA库中有什么
& & 在你下载完成之后,提取tar.gz或zip文档中的文件到本地目录。压缩文档中包含了以下内容。
& & 在UNIX系统中:
& & 在apache-mina-2.0.7目录中,你能够得到:
dist——包含MINA的jar包
doce——包含API文档和代码引用文件
lib——包含MINA要用到的所有的jar库
& & 除此之外,根目录还包含license文件和其他几个注意文件
运行你的第一个MINA程序
& & 好的,你已经下载了最新的MINA,让我们来运行MINA中附带的示例程序。
& & 把以下包加入classpath
mina-core-2.0.7.jar
mina-example-2.0.7.jar
slf4j-api-1.6.6.jar
slf4j-log4j12-1.6.6.jar
log4j-1.2.17.jar
在命令行中输入以下命令:
& & 这条指令将启动服务器。现在使用telnet连接服务器看有什么结果。
& & 输入如下telnet指令:
& & 现在你已经运行了第一个MINA程序。请尝试MINA中附带的其他示例程序。
& & 在这一章中,我们看到了基础的MINA程序结构,它对于客户端和服务器都适用。我们还涉及到了简单地TCP服务器/客户端的实现,还有UDP服务器和客户端。
& & 在接下来的一章中,我们将会讨论MINA的核心结构和一些更高级的话题。
第二章——基础
& & 在第一章中,我们简单地了解了一下MINA。在这一章中我们将会看到服务器/客户端的结构和一些它们的细节。
& & 我们将会展示一些简单地基于TCP和UDP的服务器和客户端示例。
基于MINA的应用程序结构
& & 我们也可以这样问:“一个基于MINA的程序看起来是什么样子的?”在这一章中,让我们来看一看基于MINA的程序的结构。我们试图从MINA演示中搜集信息。
& & 在这我们看到MINA是你的程序(客户端或服务器)与网络层之间的粘合剂,而程序的底层可以是基于TCP、UDP、in-VM连接,甚至是RS-232C串口协议的连接。
& & 你只需要在MINA的上层构建你的应用而不需要处理复杂的网络层问题。
& & 让我们更深入细节一些。下边的图片展示了MINA的组成,并写出了每部分做功能:
& & 很明显,基于MINA的程序分为三层:
I/O服务——实际执行I/O操作
I/O过滤器链——对所需的数据就够进行过滤/翻译,反之亦然
I/O处理——这里包含着真正的业务逻辑
& & 所以,为了构建一个基于MINA的程序,你需要:
创建I/O服务——从已有服务器选择一个或者创建你自己的
创建过滤器链——选择已经存在的过滤器或者是定义用户转换请求/回复的过滤器
创建处理器——编写业务逻辑,处理不同的信息
& & 你可以通过阅读以下两个文件更深入的了解这方面的内容:
服务器结构
客户端结构
& & 当然,MINA提供了比这更多的功能,你还需要注意其他的一些方面,像信息的编码/解码,网络配置的扩展等……我们将在下一张进一步讨论这些问题。
服务器结构
& & 在上一小节我们已经展示了MINA程序的结构。接下来让我们看一下服务器的结构。基本上,一个服务器要在特定的端口上监听客户端的请求,处理这些请求并作出回复。同时它需要创建和处理与每一个客户端之间的连接(不管你使用的是TCP还是UDP协议),这些将在第四章中有更详细的解说。
IOAcceptor监听来自网络的连接和数据包
对于一个新的连接,一个对话将会被建立,同时随后来自于这个IP地址和端口的通信都将通过这个回话进行处理
对一个会话的所有数据包,都会穿过如图所示的过滤器链。过滤器可以被用来修改数据包的内容(例如转换对象、添加或者删除信息等)。这对于为高层对象转化进出的比特数据,以及对数据的编码和解码是特别有用的。
最后数据包或转化的对象将被放在IOHandler中。IOHandler在整个业务逻辑过程中都可以被使用。
会话的创建
& & 无论什么时候一个客户端连接到MINA服务器,我们都会创建一个会话并把固定的数据保存在里边。及时协议不是面向连接的,这个会话也会被创建。下边的架构图展示了MINA如何处理对内的连接:
图挂了!!!!!
对内信息的处理
& & 我们现在解释了MINA如何处理队内的信息。
& & 假设对话已经被建立,当一些新的信息到来的时候,它将会导致一个选择器被唤醒。
简单地TCP服务器
快速开始手册
&&&&此手册将会带你构建一个基础的MINA程序。此手册将会构建一个时间服务器。本手册需要以下预备知识:
MINA 2.0.7 核心
JDK 1.5 或更高版本
[SLF4] 1.3.0 或更高版本
1.2&users:&slf4j-api.jar,&slf4j-log4j12.jar, and&&1.2.x
1.3&users:&slf4j-api.jar,&slf4j-log4j13.jar, and&&1.3.x
java.util.logging&users:&slf4j-api.jar&and&slf4j-jdk14.jar
重要:请确保对你的日志框架使用了正确的slf4j-*.jar版本。例如,&slf4j-log4j12.jar&和log4j-1.3.x.jar不能同时使用,将会出现问题。
& & 我已经在Window 2000系统和Linux系统上测试了这个程序。如果你再运行这个程序的时候有什么问题,请不要犹豫,联系我们或告诉其他的开发者。并且,本手册已经尽量独立于特定的开发环境(IDE、编辑器等)。这个手册将在你熟悉的开发环境中运行的很好。这个程序的编译命令和执行命令已经被剔除的很简洁。如果你需要学习如何编译和运行程序,请查看其他java手册。
编写MINA时间服务器
& & 我们将要建立一个叫做MinaTimeServer.java的文件。起始代码如下所示:
public class MinaTimeServer {
& & public static void main(String[] args) {
& & & & // code will go here next
& & 代码就是如此。我们简单地定义了一个用于启动程序的main方法。接下来,我们将会添加用于代码来弥补我们的服务器。首先,我们需要一个用来监听外来连接的端口。这个程序将基于TCP/IP,我们将向程序中添加一个SocketAcceptor。
& & 通过这个NioSocketAcceptor类,我们可以继续前进来定义一个处理类同时将NioSocketAcceptor绑定到一个端口上。
& & 接下来我们给配置添加一个过滤器。这个过滤器将会把所有的信息写入日志中,例如新session的创建、信息的获取、信息的发送、session的关闭。下一个过滤器是ProtocolCodecFilter。这个过滤器将会把二进制或符合协议规范的数据翻译成消息对象,反之亦然。我们使用一个已经存在的TextLine工厂,它将会为你处理关于text的信息(这样你就不用自己写代码了)。
& & 接下来,我们将会定义一个处理过程,它将会被用来接受客户端的连接,并回复当前时间。这个处理器必须是一个实现了IoHandler接口的类。对于所有使用MINA的程序来说,这个处理器将是最主要的程序,它将为接入的所有客户端提供服务。对于本手册来说,我们将继承IoHandlerAdapter类。这个类遵循适配器模式,它简化了大量为满足实现IoHandler接口的类所需要的代码。
& & 现在我们将会添加NioSocketAcceptor配置。这将使得我们可以对socket进行特定的设置,此socket将被用来接受客户端的连接。
& & 在MinaTimeServer类中有两个新行。这些方法设置了IoHandler的设置,输入缓冲区大小和会话的空闲性能。缓冲区的尺寸将被指定,它将通知潜在的操作系统应该为输入的数据分配多少空间。第二行指定多长时间监测空闲会话。在调用setidleTime的时候,第一个参数定义了当检测到会话空闲的时候要做什么,第二个参数定义了判定会话为空闲的时长。
& & 处理器类的代码如下所示:
& & 在这个类中使用的方法有exceptionCaught、messageReceived和sessionidle。exceptionCaught总会在一个处理器类中被实现,它将处理远程连接过程中产生的异常。如果这个方法没有被定义,异常将不能得到正确的处理。
& & exceptionCaught方法将简单地输出错误堆栈的信息,并关闭会话。对于大多数程序来说,这将是基本的实践,除非处理器类能从异常中恢复过来。
& & messageReceived方法将接受来自客户端的数据同时将当前时间写回给客户端。当从客户端接收到的信息是“quit”,则会话将被关闭。这个方法将向客户端打印当前时间。依赖你所使用的协议编码器,在第二个方法中传输的对象将会不同,它将会是你在session.write(Object)方法中传输的对象。如果你没有指定协议编码器,你将接收到一个IoBuffer对象,并回复一个IoBuffer对象。
& & sessionidle方法将会在会话被确定为空闲后执行,这段空闲时间我们已经使用acceptor.getSessionConfig().setidleTime(idleStatus.BOTH_IDLE,
10)中进行了设置。
& & 接下来所要做的就是定义一个socket地址,用于服务器的监听,然后启动服务器。代码如下:
测试时间服务器
& & 接下来,我们可以继续并编译这个程序。一旦你编译完这个程序,就可以启动它来测试将会发生什么。最简单的测试方式就是启动这个服务器,并用telnet连接它:
简单地TCP客户端
& & 我们已经看过了客户端的结构。接下来让我们看一下一个客户端的实现。
& & 我们用一个Sumup Client作为参考实现。
& & 我们将删除样板代码并把注意力集中在一些重要的结构上,以下是客户端的代码:
为了构建一个客户端,我们需要做下边的事情:
创建一个Connector
创建一个过滤器链
创建一个IOHandler并添加Connector
绑定到服务器
& & 下面让我们详细的说明每一步。
创建一个Connector
& & 在这我们创建了一个NIO socket连接。
创建一个过滤器链
& & 我们为Connector将过滤器添加到过滤器链中。这里我们将ProtocolCodec添加到了过滤器链中。
创建一个IOHandler
& & 在这里我们创建了一个ClientSessionHandler的示例,并将其配置到Connector上。
绑定服务器
& & 这是很重要的部分。我们连接远程服务器。至此,连接是个异步任务,我们使用ConnectFuture类确定什么时候连接完成。一旦连接完成,我们将得到连接IoSession。为了向服务器发送信息,我们需要向这个session中写数据。所有来自服务器的回复和信息将会穿过过滤器链并最终在IoHandler中得到处理。
简单地UDP服务器
& & 我们来看看org.apache.mima.example.udp包中的代码。为了让示例简单,我们只简述MINA的内容。
& & 为了构造服务器,我们需要做下边的事情:
创建一个数据包Socket用来监听来自客户端的请求(看MemoryMonitor.java)
创建一个IoHandler来处理MINA框架的事件(看MemoryMonitorHandler.java)
& & 以下是一个小片段:
& & 在这,我们创建了一个NioDatagramAcceptor来监听来自客户端的请求,同时设置IoHandler。变量“PORT”只是一个int型数据。下一步是为DtagramAcceptor添加要使用的日志过滤器。用LoggingFilter查看MINA是如何活动的是一种很好的方法。它在不同的阶段产生日志文档,提供了MINA如何工作的例证。
& & 接下来让我们插入一些UDP传输的特殊代码。我们将会设置这个acceptor使用这个地址:
& & 当然我们需要做的最后的事情就是调用bind()函数。
IoHandler的实现
& & 有三个主要事件是我们的服务器实现所感兴趣的:
& & 让我们分别看看它们的详情。
会话创建事件
& & 在会话的创建事件中,我们只是调用了addClient()函数,它在UI上添加了一个标签。
信息接收事件
& & 在信息接收事件中,我们只是将接收到的信息全部送入message中。程序能够使用这个函数发送回复、处理信息并把回复写入会话中。
会话关闭事件
在会话关闭事件中,我们只是简单地将客户端标签从UI上移除了。
简单地UDP客户端
& & 让我们看看UDP客户端的代码。
& & 为了实现这个客户端,我们需要做下边的事情:
为服务器创建Socket和Connect
设置IoHandler
收集空闲内存
向服务器发送数据
& & 我们先来看MemMonClient.java,它在org.apache.mina.example.udp.clent包中。开始的一段代码直截了当:
& & 在这我们创建了一个NioDatagramConnector,为服务器配置了handler和connect。我遇到的一个问题是你必须在InetSocketAddress对象中设置主机或者别的什么工作。这个例子是在Windows
XP上进行测试的,所以一些工作在其他地方可能会不同。接下来我们需要等待确认客户端连接到了服务器。一旦连接成功,我们可以开始等待来自服务器的数据。代码如下:
& & 在这里我们为ConnectFuture对象添加了一个监听器,当我们获得一个客户端已经连接的回调时,我们就开始写数据。向服务器写入的数据通过调用sendData函数来处理。这个方法的代码如下:
& & 这个方法每30秒就会将大量空闲内存写给服务器。在这里我们可以看到,我们分配了一个足够大的IoBuffer用来保存一个长变量然后把大量的空闲内存放入这个buffer中。随后这个buffer将会被遍历并写给服务器。
& & 我们的UDP客户端实现就完成了。
& & 在这一章中,我们看了基于MINA的程序的结构,包括客户端和服务器。同时我们涉及到了简单TCP服务器客户端和UDP服务器客户端的实现。
& & 在接下来的一章中,我们将会讨论MINA核心结构和高级主题。
第三章——IoService
& & MINA IoService——它是所有IO服务的基础类,包括服务器端和客户端。
& & 它将处理所有与你的程序之间的互动,发送和接收信息,管理会话,连接等。
& & 它是一个接口,它在服务器端有IoAcceptor实现,在客户端有IoConnector的实现。
& & 我们将会在接下来的章节中介绍这些接口:
IoService介绍
& & IoService在MINA中提供了基本的I/O服务和I/O会话管理服务。它是MINA结构中最重要的部分。IoService的实现类和子接口是绝大多数底层I/O操作进行的地方。
IoService思维导图
& & 让我们来看看IoService和她的实现类AbstractIoService类的职责。让我们用一种稍微不同的方式,先使用思维导图然后再跳进内部的工作过程。思维导图是由XMind创建的。
& & 在给出的图像中可以看出,IoService有以下职责:
会话管理:创建和删除会话,检测空闲情况
过滤器链管理:处理过滤器链,允许用户在工作过程中修改过滤器链
处理调用:当收到信息的时候调用处理器,等
统计管理:更新信息发送、bytes和其他数据的发送数量
监听器管理:管理用户可以设置的监听器
连接管理:在两个方向处理数据的传输
& & 这所有的方面在接下来的章节中将被描述。
& & IoService是所有IoConnector和IoAcceptor所提供的I/O服务和I/O对话管理的基础接口。这个接口包含了所有用于I/O相关操作的函数。
& & 让我们来深入的看一下这个接口中的各种函数
getTransportMetadata()
addListener()
removeListener()
isDisposing()
isDisposed()
getHandler()
setHandler()
getManagedSessions()
getManagedSessionCount()
getSessionConfig()
getFilterChainBuilder()
setFilterChainBuilder()
getFilterChain()
isActive()
getActivationTime()
broadcast()
setSessionDataStructureFactory()
getScheduledWriteBytes()
getScheduledWriteMessages()
getStatistics()
getTransportMetadata()
& & 这个方法返回IoAcceptor或IoConnector运行过程中的传输元数据。典型细节包括名字(nio、apr、rxtx),连接类型(无连接/有连接)等。
addListener
& & 允许添加一个用于监听与IoService有关的事件的指定监听器。
removeListener
& & 移除绑定在这个IoService的指定的IoServiceListener。
isDisposing
& & 这个方法将告知这个service当前正在被处理。由于它会花费一些时间,所以获得service的当前状态是很有用的。
isDisposed
& & 这个方法将告知这个service已经被处理过了。只有当service申请的所有资源全部被释放的时候,它才被认为处理过了。
& & 这个方法将释放所有service所申请的资源。由于它会花费一些时间,所以用户需要使用isDisposing()和isDisposed()来检测当前的service是否已经被处理完成了。
& & 当你关闭一个service的时候一定要调用dispose()。
getHandler
& & 返回service所申请的IoHandler。
setHandler
& & 设置用于处理所有事件的IoHandler。这个handler将包含你的程序逻辑!
getManagedSeesions
& & 返回当前由这个service管理的所有对话所组成的一个map。被管理的会话是那些被加入service监听器的会话。它将被用来处理空闲的会话和其他的会话,这基于用户加入监听器的会话的类型。
getManagedSessionCount
& & 返回当前由这个service所管理的会话的数量。
getSessionConfig
& & 返回对话配置。
getFilterChainBuilder
& & 返回过滤器链创建者。如果想在会话建立的时候向其中添加新的过滤器,这个函数将是很有用的。
getFilterChain
& & 返回当前这个service的默认过滤器链。
& & 告知当前service是活动的还是不活动的。
getActivationTime
& & 当service获得的时候返回活动时间。当service已经不在活动的时候,这个函数将返回它最后一次活动的时间。
& & 向service管理的所有会话发送信息。
setSessionDataStructureFactory
& & 这个方法将设置IoSessionDataStructureFactory,此工厂将为当前service所创建的会话提供相关数据结构。
getScheduledWriteBytes
& & 返回将要写的比特数(这些比特保存在内存中,它们等待着socket的写)。
getScheduledWriteMessages
& &&返回将要写的信息数(这些信息保存在内存中,它们等待着socket的写)。
getStatistics
& & 为service返回IoServiceStatistics对象。
IoService细节
& & IoService在MINA中被两个非常重要的类所实现:
IoAcceptor
IoConnector
& & 为了构建一个服务器,你需要选择一个IoAccrptor接口的实现。为了构建一个客户端,你需要一个IoConnector接口的实现。
IoAcceptor
& & 基本上这个接口被这样命名是因为accept()方法,它负责在一个服务器和客户端之间创建链接。服务器结构对内的请求。
& & 在一些情况下,我们可以将这个借口命名为“Server”(这个新的名字来自MINA 3.0)。
& & 我们有很多的方式来完成传输任务(TCP/UDP……),所以对于这个接口也有多个实现。它不大可能需要你自己写一个实现。
& & 我们对它有以下实现类:
NioSocketAcceptor:非阻塞的Socket传输IoAcceptor
NioDatagramAcceptor:非阻塞的UDP传输IoAcceptor
AprSocketAcceptor:阻塞的Socket传输IoAcceptor,基于APR
VmPipeSocketAcceptor:in-VM IoAcceptor
& & 只需要选择一个你需要的。
& & 以下是IoAcceptor的实现类图解:
创建Acceptor
& & 首先你要选择你需要的IoAcceptor的实例类型。你的这个选择将决定你在后期使用什么样的网络协议。让我们来看一个它是如何工作的示例:
& & 就是这样!你已经创建了一个TCP服务器。如果你想创建一个UDP服务器,你只需要简单地更换第一行代码:
& & 这个服务器可以通过调用dispose()方法停止。服务器只有在所有挂起的会话都被解决之后才会停止:
& & 你也可以通过向这个方法传送一个loolean值然后等待所有的线程都被执行:
& & 你可以通过调用以下方法获得IoService的状态:
isActive():如果服务可以接受请求则值为true
isDisposing():当dispose()方法已经被执行过则它返回true。当服务已经停止的话它将没有返回值(一些会话可能已经被执行了)。
isDisposed():当dispose(boolean)方法已经被执行则它返回true,此时处理的线程已经完成了。
管理IoHandler
& & 如果服务实例化后你可以向其添加IoHandler或获取与其绑定的IoHandler。你只需要调用setHandler(IoHandler)或getHandler()方法。
管理过滤器链
& & 如果你想要管理过滤器链,你需要调用getFilterChain()方法。以下是一个例子:
& & 你也可以事先创建一个过滤器链,然后再将它加入服务:
IoConnector
& & 就如我们需要IoAcceptor来构建服务器,你需要为客户端实现IoConnector接口。同样,我们已经有了很多实现类:
NioSocketConnector:&非阻塞的Socket传输IoConnector
NioDatagramconnector:&非阻塞的UDP传输IoConnector
AprSocketConnetor:阻塞的Socket传输IoConnector,基于APR
ProxyConnector:一个提供代理支持的IoConnector
SerialConnector:一个提供串口传输功能的IoConnector
VmPipeConnector:in-Vm IoConnector
& & 只需要选择一个你需要的。
& &&&以下是IoConnector的实现类图解:
第四章——会话
& & 会话是MINA的核心:每当客户端连接到服务器,一个新的会话就会被建立,它会被保存在内存中直到客户端断开连接。
& & 会话用来保存与链接相关的固定信息,外加一些服务器恢复客户端请求时所要用到的信息,还有在整个会话生存期中有用的信息。
& & 会话有它自己的状态,这种状态会不断的变化。
已连接的:会话已经被创建并且可用
空闲:这个回话在一段时间(这段时间是可配置的)里没有处理任何请求
读空闲:在一段时间里没有读操作
写空闲:在一段时间里没有写操作
全部空闲:在一段时间里没有读写操作
正在关闭:会话正在被关闭(剩余的信息将被刷新清理,会话将被终止)
已关闭:会话已经被关闭了,不能再通过它做任何事情
& & 下边的图片展示了所有的状态以及他们之间的转化关系:
& & 我们可以为一个会话设置很多不同的参数:
接收缓存的大小
发送缓存的大小
写超时时间
& & 还有一些其他的配置,它们将依赖于所使用的传输协议类型(看第六章——传输协议)
管理用户定义的属性
& & 为了以后的使用保存一些数据是必须的。这些数据使用与会话相联系的专用数据结构来存储。这是一个数值对,它可以用来存储开发人员希望保存的数据。
& & 例如,如果你想记录从会话创建以来收到的请求次数,这将很容易将其保存到一个map中:只需要创建一个与这个值相联系的值对。
& & 我们又一种方式来处理存储在会话中的属性:一个属性是一个key/value值对,我们可以在seesion容器中添加、删除或读取这个值对。
& & 这个容器在会话创建的时候将被自动建立,同时在会话关闭的时候将被自动删除。
& & 就像我们说的,这个容器是一个key/value值对,它默认是Map类型的,如果有人想处理长寿命数据或想在数据很大的时候避免保存所有的数据,此时我们也可以定义其他的数据结构:在会话创建的时候,我们可以实现一个接口和一个工厂以用来创建这个容器。
& & 下边的代码段演示了如何在会话初始化的时候创建一个容器:
& & 下边的代码实现了一个工厂接口,用它我们可以定义另外类型的容器:
& & 每一个会话都与一个过滤器链相联系,过滤器会处理接收到的请求和发送出去的消息。这些过滤器链为每一个会话指定,尽管大多数情况,我们对存在的会话将使用相同的过滤器链。
& & 无论如何,我们可以为一个单独的会话动态的修改过滤器链,例如为一个指定的会话添加一个Logger过滤器。
& & 一个对话通常还会保存一些数据,这些数据显示了这个会话都做了什么:
接收到和发送出去的比特数
接收到和发送出去的信息数
& & 还有一些其他的有用信息。
& & 最后也是相当重要的一部分,一个会话与一个Handler相联系,它负责调度你程序中的信息。handler还会通过调用write()方法使用会话发送包装好的会话信息。
第五章——过滤器
& & IoFilter是MINA中的一个核心结构,它提供了非常重要的规则。它过滤IoService和IoHandler之间的所有I/O事件和请求。如果你有web编程的经验,你可能会认为它是Servlet过滤器的堂兄弟。
&&&&许多拆箱即用的过滤器是为了加快网络程序开发的步伐,通常会使用以下过滤器简化典型的横切关注点(AOP即面相切面编程,其主要意图是把业务无关的东西剥离出去,我们比较常用的比如事务处理,把开始和提交事务形象的当成一个“点”或者“面”,比如spring。实现原理主要有动态代理和修改字节码):
LoggingFilter记录所有事件和请求的日志
ProtocolCodecFilter过滤器将对内的ByteBuffer信息转换成POJO(简单地Java对象),反之亦然
CompressionFilter过滤器压缩所有的数据
SSLFilter过滤器将添加SSL-TLS-StartTLS支持
还有更多……
& & 在这本手册中,我们将为一个真实世界示例逐步实现一个IoFilter。通常实现一个IoFilter是很容易的,但是你还需要知道MINA内部结构的细节。我们将介绍一些相关的内部属性。
现有的过滤器
& & 我们已经实现了很多过滤器。下表列出了所有已经存在的过滤器,还有它们的简短描述。
有选择的重载事件
& & 你可以扩展一个IoAdaptor而不是直接实现一个IoFilter。除非重载了,否则接收到的事件将直接传送到下一个过滤器:
转换一个写请求
& & 如果你想通过IoSession.write()方法来转换一个对内的请求信息,事情将变得很棘手。例如,如果你的过滤器希望将HighLevelMessage转换成LowLevelMessage,而此时是由HighLevelMessage对象调用IoSession.write()方法。你可以向你的过滤器的filterWrite()方法中插入适当的转换代码,并认为这样就行了。无论如何,你还需要关注messageSent事件,因为IoHandler或接下来的过滤器将会期待HighLevelMessage调用messageSent()方法,并把HighLevelMessage作为其参数,因为当调用者实际上写了HighLevelMessage的时候,调用者却发送了LowLevelMessage是不理智的。因此,所以当你的过滤器执行转换工作的时候你需要实现filterWrite()和messageSent()两个方法。
& & 请注意即使输入的对象和输出的对象即使完全相同,你仍然需要实现相似的机制,因为IoSession.write()的调用者将指望着它messageSent()处理函数中所写的东西。
& & 让我们假设你定义了一个将String转换成char[]数据的过滤器。你的过滤器的filterWrite()方法看起来如下:
& & 现在,我们在messageSent()中要做相反的事情:
& & 这个Sting-to-ByteBuffer转换如何?我们可以做得更有效,因为我们不需要重写原来的信息(String)。无论如何,下边的代码比我们给出的示例更复杂一些:
& & 如果你是用MINA 2.0,它将与1.0和1.1版有所不同。详细信息请查看CompressionFilter和RequestResponseFilter的信息。
过滤sessionCreated事件的时候要小心
& & sessionCreated是一个特殊的事件,它必须在I/O处理线程中进行处理(看线程模块模板)。永远不要把sessionCreated事件传给其他的线程。
小心空缓冲
& & 在一些情况中MINA将空缓冲最为内部的信号。空缓冲有时候会成为错误,因为有时候它是因为各种异常造成的,例如IndexOutOfBoundsException。这一小节将介绍如何避免这种意外情况。
& & ProtocolCodecFilter使用一个空缓冲(buf.hasRemaining() = 0)来标记信息的结束。如果你的过滤器被放在protocolCodecFilter之前,如果你的过滤器实现可能会产生空换成的异常的时候,请在protocolCodecFilter中确保它发送给另外一个过滤器:
& & 你是否需要向每一个过滤器中都插入if判断块呢?很幸运,你不需要这样做。下边是一些处理空缓冲的金玉良言:
如果你的代码不会出任何问题包括空缓冲,那么你不需要添加if块
如果你的过滤器被放在ProtocolCodecFilter之后,你不需要添加if块
其他情况,你就需要添加if块了
& & 如果你需要if判断块,请记住你不需要总是按照上边例子写。你可以在任何地方检测是否有空缓冲,只要你的过滤器不会抛出意想不到的异常就可以了。
第六章——传输
& & APR(Apache Portable Runtime/线程池)提供更好的扩展性、性能和与本地系统的更好整合。APR被MINA所支持。在这一章中,我们将接触到如何使用MINA中的APR进行传输。我们将使用时间服务器来展示。
APR传输依赖以下部件
&&&&APR库——从apache的网站下载并安装适合于你的平台的库。
& & JNI wrapper(tomcat-apr-5.5.23.jar)jar包附带在发行包中
& & 将本地库的地址加入PATH环境变量中
使用APR传输
& & 让我们看一看基于NIO的时间服务器实现是怎样的:
& & 让我们看一看如何使用APR传输:
& & 我们只是简单地将NioSocketAcceptor改为了AprSocketAceeptor。就只这样,现在我们的时间服务器就可以使用APR传输了。
& & 剩余部分仍然是相同的。
& & 在MINA 2.0中你可以像连接到一个TCP/IP端口一样连接到一个串口。
获得MINA 2.0
& & 你可以下载最新的MINA编译版(2.0.2)
& & 如果你更趋向于从代码自己构建MINA,请查阅开发者手册。
有用的信息
& & 再从一个Java程序访问串口之前你需要一个本地库(.Dll或.so这依赖于你的系统)。MINA使用来自RXTX.org的库:
& & 把正确的.dll或.so库文件放到jre/lib/i386目录中,或者使用-Djava.library.path=argument来指定你的库所存放的位置。
有用的信息
& & mina-transport-serial.jar并不包含在所有的发布包中。你可以单独下载这个包
连接到串口
& & 对于串口连接MINA只是提供了一个IoConnector,这是因为连接媒介点对点的特性。
& & 对于这一点,你应该已经看过MINA手册了。
& & 现在,如果你想要连接到串口则需要一个SerialConnector:
& & 与SocketConnector没有太多的不同。
& & 让我们来为我们的串口创建一个地址以用来连接。
& & 第一个参数是你的端口标示符。对于Windows计算机,串口被称为“COM1”、“COM2”等等……对于Linux和其他的UNIX系统:“/dev/ttyS0”、“/dev/ttyS1”、“/dev/ttyUSB0”。
& & 剩下的参数依赖于你所驱动的设备以及它们的特点。
& & 一旦这些工作完成后,就可以连接了:
& & 瞧!其他的所有工作都与之前一样,你可以插入你自己的过滤器和代码。为了学习有关RS232的更多知识,请查看:
第七章——Handler
& & MINA处理所有的I/O事件。这个接口是过滤器链最后所做活动的集线器。
& & IoHandler有以下函数:
sessionCreated
sessionOpened
sessionClosed
sessionIdle
exceptionCaught
messageReceived
messageSent
sessionCreated事件
& & Session创建时间将在一个新的连接被建立时发生。对于TCP它是连接被接受的结果,对于UDP每次当接收到UDP包他都会发生。这个函数可以被用来初始化会话属性,还可以为一个特定的链接执行一次操作。
& & 这个函数在I/O处理线程环境中被调用,所以它需要被实现为花费很少的时间,这样同一个线程才能够处理多个会话。
sessionOpened事件
& & 当一个连接被打开的时候Seesion事件就会发生。它通常在sessionCreated事件后被调用。如果一个线程模块被设置过,这个函数将在不同于I/O处理线程的线程中被调用。
seesionClosed事件
& & 当一个会话被关闭的时候这个时间将发生。会话的清理工作可以在这里进行。
sessionIdle事件
& & 当一个会话处于空间状态的时候这个时间将发生。这个函数不会被UDP传输调用。
exceptionCaught事件
& & 当用户代码或MINA代码抛出异常的时候这个事件将会发生。如果异常是IOException,则链接将被关闭。
messageReceived事件
& & 当接收到信息的时候这个事件将会发生。这将是大多数程序处理数据的地方。在这里你需要注意你所关注的信息。
messageSent事件
& & 当一个信息或者说是一条回复被发送的时候,这个事件会发生(调用IoSession.write()的时候)。
二部分——MINA核心
第八章——IoBuffer
& & MINA使用一个byte缓冲。
& & 这是一个ByteBuffer的替代者。MINA不直接使用NIO ByteBuffer的原因有两个:
他没有提供有用的getters和putters函数,例如fill、get/putString、get/putAsciiInt()
用它来写可变长的数据是很困难的,因为ByteBuffer有固定的长度
& & 这在MINA 3中将有所改变。要在NIO ByteBuffer的上层拥有自己的包装的主要原因是MINA需要可扩展的buffer。这是一个非常不好的决定。Buffers就是buffers:一个用于存储临时数据的临时空间。还有很多其它的解决方案,例如定义一个依赖于NIO ByteBuffers List的包装,而不是仅仅为了我们需要可扩展的buffer而将已存在的buffer拷贝到一个更大的buffer中。
& & 在整个数据过滤过程中使用一个InputStream或许比使用一个ByteBuffer更好,因为它并不隐含有关存储的数据是什么类型的信息:它可以是一个byte数组、strings或messages……
& & 最后也是相当重要的一个方面,目前的实现达到了一个目的:零拷贝策略(一旦我们已经从socket读取了数据,我们将避免随后的数据复制)。如果我们使用可扩展的byte buffers,如果我们管理大信息的时候肯定会拷贝这些数据。假设MINA
ByteBuffer只是NIO ByteBuffer的一个上层包装,直接使用buffers将会是一个问题。
IoBuffer操作
申请一个新Buffer
& & IoBuffer是一个抽象类,因此不能被直接实例化。为了申请一个IoBuffer,我们需要使用两个allocate()函数中的两个。
& & allocate()方法需要一个或者两个参数。第一种使用两个参数:
capacity——buffer的容量
direct——buffer的类型。true的时候直接获得buffer,false的时候获得buffer堆
& & 默认的buffer申请工作是由SimpleBufferAllocator完成的。
& & 作为另外一种选择,下边的方式也是可以的:
& & 当使用第二种格式的时候,不要忘了设置默认的buffer类型,否则你将默认获得buffer堆。
创建自动可扩展的Buffer
& & 对于Java NIO API来说,创建一个可扩展的buffer并不是很容易,因为buffers有固定的大小。拥有一个可扩展的buffer对于一个网络应用程序来说是一个很大的优点。为了说明这个,IoBuffer已经介绍了autoExpand属性。它将自动扩展它的容量和限制值。
& & 让我们看一下如何创建一个可自动扩展的buffer:
& & 如果在上边的例子中被编码的数据超过了8 bytes,那么IoBuffer将重新分配额外的ByteBuffer。它的容量将会加倍,它的范围限制将增加到所写入的字符串的最后一个位置。这种行为很类似于StringBuffer类的工作。
& & 这种机制在MINA 3.0中很可能被移除,因为它并不是最好的解决可增加buffer大小的方法。它很可能被隐含着List的InputStream或固定大小的ByteBuffers数组所代替。
创建自动收缩的Buffer
& & 有一种情况是需要从buffer中释放已经申请的bytes,以此来节省内存。IoBuffer提供了autoShrink属性来满足这个需求。如果autoShrink被启动,那么当compact()被调用的时候buffer只有它全部容量的一般,而且只有当前容量1/4或者更少的部分被使用。为了手动缩小buffer,使用shrink()方法。
让我们来看看这个过程:
& & 我们已经初步申请了一个容量为16的buffer,而且把autoShrink属性设置成了true。
& & 让我们看看输出:
& & 让我们休息一下,分析一下输出:
更具我们的配置初始化buffer容量是16。在内存中这变成了buffer的最小值
在调用shrink()之后,容量剩余16,所以容量永远不会小于buffer的最小值
随后把容量增加到32,容量变为了32
调用shrink(),将容量缩小为16,因此消除了额外的存储
& & 此外,这个机制是默认的,不需要明确的告诉buffer它可以缩小。
Buffer分配
& & IoBufferAllocater负责分配和管理buffers。为了精确地控制buffer的分配,实现IoBufferAllocater接口。
& & MINA附带以下IoBufferAllocater的实现:
SimpleBufferAllocator(默认)——每次都创建一个新buffer
CachedBufferAllocator——缓存可能会被重用的buffer
& & 对于新的JVM,使用缓存的IoBuffer不大可能会提高性能。
& & 你可以实现你自己的IoBufferAllocator并在IoBuffer上同样调用setAllocator()。
第九章——编解码过滤器
& & 这部分教程将解释为什么和怎么使用ProtocolCodecFilter。
为什么使用ProtocolCodecFilter?
TCP连接保证所有数据包按照正确的顺序交付。但是它并不担保发送者的发送写操作在接受者那边会有一个写事件与其对应。看和。MINA术语:发送者没有ProtocolCodecFilter过滤器的一个IoSeesion.write(Object message)操作将导致接受者方面多个messageReceived(IoSession session,Object message)事件的发生,而多个IoSession.write(Object message)的调用将导致一个单独的messageReceived事件。当你的服务器和客户端运行在同一个主机上(或者同一个本地网络上)的时候,你可能不会遇到这个问题,但是你的程序需要能够应付这种问题。
大多数网络程序需要一种方法来判断信息什么时候结束和下一条信息什么时候开始
你可以在你的IoHandler中实现多有的这些逻辑,但是添加一个ProtocolCodecFilter将会使得你的代码更整洁并便于维护
它允许你将协议逻辑和业务逻辑(IoHandler)分离开来
如何使用?
& & 你的程序主要是接收一串bytes数据,你需要将这些数据转换进入message(高层的对象)中。
& & 这有三种通用技术能够将byte流转换成message:
使用固定长度的信息
使用固定长度的头来标明数据体的长度
使用一个定界符,例如许多基于文本的协议为每一个信息附加一个换行符()
& & 在本手册中,我们使用第一个和第二个方法,因为它们实现起来很简单。随后我们将会看一下定界符。
& & 我们将会开发一个(一点用都没有的)图形产生服务器来演示如何实现你自己的协议编解码器(ProtocolEncoder、ProtocolDecoder和ProtocolCodecFactory)。这个协议非常简单。以下是请求信息的结构:
width:请求图片的宽度(网络字节序的整型)
height:请求图片的高度( 网络字节序的整型&)
numchars:产生的字符数量( 网络字节序的整型&)
& & 服务器将回复两张客户端所请求尺寸的图像,并把所要求的字符打印在上边。以下是回复信息的结构:
& & 概述请求和回复数据编码和解码所要用到的类:
ImageRequest:一个简单地对象,它代表我们的ImageServer的请求
ImageRequestEncoder:将ImageRequest对象编码成协议所指定的数据(由客户端使用)
ImageRequestDecoder:将协议指定的数据解码成ImageRequest对象(由服务器使用)
ImageResponse:一个简单地对象,它代表我们的ImageServer的回复
ImageResponseEncoder:被服务器用来编码ImageResponse对象
ImageResponseDecoder:被客户端用来解码ImageResponse对象
ImageCodecFactory:这个类创建必须的编码器和解码器
以下是ImageRequest类:
& & 编码工作往往比解码工作要简单,所以让我们从ImageRequestEncoder:
MINA将会为IoSession写队列中的所有信息调用编码函数。如果我们的客户端只写ImageRequest对象,我们可以安全的将ImageRequest转换为message。
我从堆中申请一个IoBuffer。最好避免使用直接缓冲区,因为堆缓冲的性能更好。看&
你不需要释放缓冲区,因为MINA会替你做这件事。看&&
在dispose()方法中你需要在为一个指定的会话编码的时候释放所有申请到的资源。如果没什么要释放的,你可以让你的编码器继承ProtocolEncoderAdapter。
& & 现在,让我们看看解码器。CumulativeProtocolDecoder对编写你自己的解码器将很有帮助:它将会缓冲所有获得的数据,直到你决定了这些数据可以做什么。在这个例子中,信息有固定的长度,所以这种情况下等待所有数据有效是最简单的:
每次一个完整的信息被解码后,你需要将它写给ProtocolDecoderOutput,这个信息将会顺序穿过所有的过滤器,并最终到达你的IoHandler.messageReceived方法
你不需要释放IoBuffer
如果没有足够的有效信息来进行解码工作,它将会返回false
& & 回复通常也是一个简单地类:
& & 回复的编码通常很繁琐:
当无法提前计算IoBuffer长度的时候,你可以通过调用buffer.setAutoExpand(true)方法使用一个自动扩展的buffer
& & 现在让我们看看回复的解码过程:
我们将解码操作的状态保存在一个会话变量中。我们也可以将它保存在解码器对象中,但是这样做有以下几个缺点:
每一个IoSession将需要它们自己的解码器实例
MINA保证不会有多余一个线程同时对同一个会话执行decode()操作,但它不保证永远都由同一个进程完成这个工作。假设一个数据的一部分被线程1处理,结果被认定为还不能被解码,此时当另外一部分到达的时候,它可能会被其他的线程处理。为了避免这个明显的问题,你需要同步访问这个解码器的状态(IoSession的变量保存在一个ConcurrentHashMap中,它对于其他线程是自动可见的)
来自邮件列表的讨论得出了以下结论:选择将解码器状态保存在IoSession中还是解码器对象自身中更像一种个人口味。为了却不会有两个线程对同一个会话进行解码工作,MINA需要做一些同步工作,这能够确保你不会遇到上边提到的问题。
当你的协议使用一定长度的前缀的时候,IoBuffer.prefixedDataAvailable()方法将很方便,它支持1、2或4位的前缀
对回复的解码工作完成之后不要忘了重置解码器的状态(移除会话参数是达到这个目的的另一个方法)
& & 如果回复是由单独的图像组成的,我们将不需要存储解码器状态:
& & 接下来让我们把所有的组合到一起:
对于每一个会话,MINA将会为编码器和解码器调用ImageCodecFactory方法
当我们的编码器和解码器没有会话状态的时候,所有会话分享一个实例是安全的
& & 以下是服务器将如何使用ProtocolCodecFactory:
& & 客户端的时候是完全相同的:
& & 对于完整的实例,我将为服务器IoHandler添加代码:
& & 对于编码器和解码器还有很多要说的。但是我们希望这部分教程已经能够让你开始工作了。我们会在以后试着加入一些有关DemuxingProtocolCodecFactory的解说。到时候我们将看一下如何使用定界符来代替前缀。
第十章——Executor过滤器
& & MINA 1.X版本中让用户在Acceptor层定义线程模块。它是Acceptor配置的一部分。这导致了复杂性,MINA团队决定将其移除,并使用更通用的系统代替,基于过滤器的:ExecutorFilter。
ExecutorFilter类
& & 这个类实现了IoFilter接口,主要的,它包含一个Executor,它将对内的事件传播给一个线程池。这允许程序使用更有效的方法来处理数据,例如CPU密集型操作。
& & 这个过滤器可以被用在handler之前,假设大多数的处理将会在你的程序中完成,或者是在CPU密集型过滤器之前(例如CodecFilter)。
& & 期待更多内容……
第十一章——SSL过滤器
& & 等待完成……
第十二章——Logging过滤器
& & Apache MINA允许开发者在基于MINA的程序中使用自己的日志系统。
& & MINA采用Simple Logging Facade for Java(SLF4J)。这种日志体系允许你使用多种日志系统。你可以使用log4j、java.util.logging系统等。这种体制最好的地方就是当你在开发过程中想把java.util.logging换成log4j,根本就不需要修改你的代码。
选择正确的JARs
& & SLF4J使用一个基本的绑定。也就是说每一个日志框架都有与其对应的JAR文件。你可通过选择对应的JAR包来使用你最喜欢的日志框架。以下是各框架对应的JAR:
记住下边几条:
大多数实现都要用到slf4j-api.jar
重要:你只能将一种实现的JAR包放到class path中;否则你的程序可能会发生不可知的错误
slf4j-api.jar和slf4j-jar的版本不许相同
& & 一旦配置好,你就可以继续配置你选的真实的日志系统了。
重载Jakarta Commons日志
& & SLF4J提供了一种转换已存在程序的方法,SLF4J使用Jakarta Commons Logging进行这个转变,他不需要修改代码。只需要从class
path中删除commons-loggong JAR包,然后添加jcl104-voer-slf4j.jar。
& & 在这个例子中我们将使用log4j日志系统。我们把下边的片段加入log4j.properties文件中对项目进行配置:
& & 这个文件将被放在我们项目的源代码目录中。如果你使用IDE,你应当把文件加入JVM的classpath中,以便你测试代码。
& & 尽管这里展示了如何配置一个IoAcceptor使用日志系统,要知道SLF4J可以被用在你的程序的很多地方,以用来生成对你来说有用的日志信息。
& & 接下来我们配置一个简单的服务器实例用来生成一些日志。在这里我们使用了EchoServer实例,并把日志系统加入了这个类中:
& & 我们可以看到我们删除了addLogger方法,同时在EchoServer中添加了两行。作为LoggingFilter的参照物,你可以为与IoAcceptor相联系的handler中配置每种事件对应的日志等级。为了制定事件所引发的日志类型和等级,在LoggingFilter中有一个叫做setLogLevel(IoEventType,LoLevel)的方法。下边是这个方法可用的参数:
& & 以下是等级描述:
& & 有了这些信息,你就能够构建一个基础的系统,并为了产生你自己系统的日志而扩展上边的实例。
第三部分——MINA高级
第十三章——Debugging
& & 等待完成……
第十四章——状态机
第十五章——代理
第十六章——JMX集成
第十七章——Spring集成
下载:Mina2.0用户手册中文版(第一二两部分)
阅读(1404) | 评论(0) | 转发(0) |
下一篇:没有了
相关热门文章
给主人留下些什么吧!~~
请登录后评论。}

我要回帖

更多关于 视频编码解码器 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信