在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS等,这些名词之间到底是些什么关系呢,它们背后到底是基于什么原理实现的呢,了解这些是实现分布式服务框架的基础知识,而如果在性能上有高的要求的话,那深入了解这些技术背后的机制就是必须的了,在这篇blog中我们将来一探究竟,抛砖引玉,欢迎大家提供更多的实现远程通讯的技术和原理的介绍。
基本原理
要实现网络机器间的通讯,首先得来看看计算机系统网络通信的基本原理,在底层层面去看,网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络IO来实现,其中传输协议比较出名的有http、tcp、udp等等,http、tcp、udp都是在基于Socket概念上为某类应用场景而扩展出的传输协议,网络IO,主要有bio、nio、aio三种方式,所有的分布式应用通讯都基于这个原理而实现,只是为了应用的易用,各种语言通常都会提供一些更为贴近应用易用的应用层协议。
应用级协议
远程服务通讯,需要达到的目标是在一台计算机发起请求,另外一台机器在接收到请求后进行相应的处理并将结果返回给请求端,这其中又会有诸如one way request、同步请求、异步请求等等请求方式,按照网络通信原理,需要实现这个需要做的就是将请求转换成流,通过传输协议传输至远端,远端计算机在接收到请求的流后进行处理,处理完毕后将结果转化为流,并通过传输协议返回给调用端。
原理是这样的,但为了应用的方便,业界推出了很多基于此原理之上的应用级的协议,使得大家可以不用去直接操作这么底层的东西,通常应用级的远程通信协议会提供:
1、为了避免直接做流操作这么麻烦,提供一种更加易用或贴合语言的标准传输格式;
2、网络通信机制的实现,就是替你完成了将传输格式转化为流,通过某种传输协议传输至远端计算机,远端计算机在接收到流后转化为传输格式,并进行存储或以某种方式通知远端计算机。
所以在学习应用级的远程通信协议时,我们可以带着这几个问题进行学习:
1、传输的标准格式是什么?
2、怎么样将请求转化为传输的流?
3、怎么接收和处理流?
4、传输协议是?
不过应用级的远程通信协议并不会在传输协议上做什么多大的改进,主要是在流操作方面,让应用层生成流和处理流的这个过程更加的贴合所使用的语言或标准,至于传输协议则通常都是可选的,在java领域中知名的有:RMI、XML-RPC、Binary-RPC、SOAP、CORBA、JMS,来具体的看看这些远程通信的应用级协议:
--------------------------------------------------------------------------------------------------------------------------------------------------
RMI
RMI是个典型的为java定制的远程通信协议,我们都知道,在single vm中,我们可以通过直接调用java object instance来实现通信,那么在远程通信时,如果也能按照这种方式当然是最好了,这种远程通信的机制成为RPC(Remote Procedure Call),RMI正是朝着这个目标而诞生的。
来看下基于RMI的一次完整的远程通信过程的原理:
1、客户端发起请求,请求转交至RMI客户端的stub类;
2、stub类将请求的接口、方法、参数等信息进行序列化;
3、基于socket将序列化后的流传输至服务器端;
4、服务器端接收到流后转发至相应的skelton类;
5、skelton类将请求的信息反序列化后调用实际的处理类;
6、处理类处理完毕后将结果返回给skelton类;
7、Skelton类将结果序列化,通过socket将流传送给客户端的stub;
8、stub在接收到流后反序列化,将反序列化后的Java Object返回给调用者。
来看jboss-remoting对于此过程的一个更好的图示:
根据原理来回答下之前学习应用级协议带着的几个问题:
1、传输的标准格式是什么?
是Java ObjectStream。
2、怎么样将请求转化为传输的流?
基于Java串行化机制将请求的java object信息转化为流。
3、怎么接收和处理流?
根据采用的协议启动相应的监听端口,当有流进入后基于Java串行化机制将流进行反序列化,并根据RMI协议获取到相应的处理对象信息,进行调用并处理,处理完毕后的结果同样基于java串行化机制进行返回。
4、传输协议是?
Socket。
--------------------------------------------------------------------------------------------------------------------------------------------------
XML-RPC
XML-RPC也是一种和RMI类似的远程调用的协议,它和RMI的不同之处在于它以标准的xml格式来定义请求的信息(请求的对象、方法、参数等),这样的好处是什么呢,就是在跨语言通讯的时候也可以使用。
来看下XML-RPC协议的一次远程通信过程:
1、客户端发起请求,按照XML-RPC协议将请求信息进行填充;
2、填充完毕后将xml转化为流,通过传输协议进行传输;
3、接收到在接收到流后转换为xml,按照XML-RPC协议获取请求的信息并进行处理;
4、处理完毕后将结果按照XML-RPC协议写入xml中并返回。
图示以上过程:
同样来回答问题:
1、传输的标准格式是?
标准格式的XML。
2、怎么样将请求转化为传输的流?
将XML转化为流。
3、怎么接收和处理流?
通过监听的端口获取到请求的流,转化为XML,并根据协议获取请求的信息,进行处理并将结果写入XML中返回。
4、传输协议是?
Http。
--------------------------------------------------------------------------------------------------------------------------------------------------
Binary-RPC
Binary-RPC看名字就知道和XML-RPC是差不多的了,不同之处仅在于传输的标准格式由XML转为了二进制的格式。
同样来回答问题:
1、传输的标准格式是?
标准格式的二进制文件。
2、怎么样将请求转化为传输的流?
将二进制格式文件转化为流。
3、怎么接收和处理流?
通过监听的端口获取到请求的流,转化为二进制文件,根据协议获取请求的信息,进行处理并将结果写入XML中返回。
4、传输协议是?
Http。
--------------------------------------------------------------------------------------------------------------------------------------------------
SOAP
SOAP原意为Simple Object Access Protocol,是一个用于分布式环境的、轻量级的、基于XML进行信息交换的通信协议,可以认为SOAP是XML RPC的高级版,两者的原理完全相同,都是http+XML,不同的仅在于两者定义的XML规范不同,SOAP也是Webservice采用的服务调用协议标准,因此在此就不多加阐述了。
--------------------------------------------------------------------------------------------------------------------------------------------------
CORBA
Common Object Request Broker Architecture(公用对象请求代理[调度]程序体系结构),是一组用来定义“分布式对象系统”的标准,由OMG(Object Menagement Group)作为发起和标准制定单位。CORBA的目的是定义一套协议,符合这个协议的对象可以互相交互,不论它们是用什么样的语言写的,不论它们运行于什么样的机器和操作系统。
CORBA在我看来是个类似于SOA的体系架构,涵盖可选的远程通信协议,但其本身不能列入通信协议这里来讲,而且CORBA基本淘汰,再加上对CORBA也不怎么懂,在此就不进行阐述了。
--------------------------------------------------------------------------------------------------------------------------------------------------
JMS
JMS呢,是实现java领域远程通信的一种手段和方法,基于JMS实现远程通信时和RPC是不同的,虽然可以做到RPC的效果,但因为不是从协议级别定义的,因此我们不认为JMS是个RPC协议,但它确实是个远程通信协议,在其他的语言体系中也存在着类似JMS的东西,可以统一的将这类机制称为消息机制,而消息机制呢,通常是高并发、分布式领域推荐的一种通信机制,这里的主要一个问题是容错(详细见ErLang论文)。
来看JMS中的一次远程通信的过程:
1、客户端将请求转化为符合JMS规定的Message;
2、通过JMS API将Message放入JMS Queue或Topic中;
3、如为JMS Queue,则发送中相应的目标Queue中,如为Topic,则发送给订阅了此Topic的JMS Queue。
4、处理端则通过轮训JMS Queue,来获取消息,接收到消息后根据JMS协议来解析Message并处理。
回答问题:
1、传输的标准格式是?
JMS规定的Message。
2、怎么样将请求转化为传输的流?
将参数信息放入Message中即可。
3、怎么接收和处理流?
轮训JMS Queue来接收Message,接收到后进行处理,处理完毕后仍然是以Message的方式放入Queue中发送或Multicast。
4、传输协议是?
不限。
基于JMS也是常用的实现远程异步调用的方法之一。
可选实现技术
当然,在上面的原理中并没有介绍到所有的java领域可选的远程通信协议了,例如还有EJB采用的ORMI、Spring自己定义的一个简单的Http Invoker等等。
看完原理后我们再来看看目前java领域可用于实现远程通讯的框架或library,知名的有:JBoss-Remoting、Spring-Remoting、Hessian、Burlap、XFire(Axis)、ActiveMQ、Mina、Mule、EJB3等等,来对每种做个简单的介绍和评价,其实呢,要做分布式服务框架,这些东西都是要有非常深刻的了解的,因为分布式服务框架其实是包含了解决分布式领域以及应用层面领域两方面问题的。
当然,你也可以自己根据远程网络通信原理(transport protocol+Net IO)去实现自己的通讯框架或library。
那么在了解这些远程通讯的框架或library时,会带着什么问题去学习呢?
1、是基于什么协议实现的?
2、怎么发起请求?
3、怎么将请求转化为符合协议的格式的?
4、使用什么传输协议传输?
5、响应端基于什么机制来接收请求?
6、怎么将流还原为传输格式的?
7、处理完毕后怎么回应?
--------------------------------------------------------------------------------------------------------------------------------------------------
JBoss-Remoting
Jboss-remoting是由jboss编写的一个java领域的远程通讯框架,基于此框架,可以很简单的实现基于多种传输协议的java对象的RPC。
直接来回答问题:
1、是基于什么协议实现的?
JBoss-Remoting是个通讯框架,因此它支持多种协议方式的通信,例如纯粹的socket+io方式、rmi方式、http+io方式等。
2、怎么发起请求?
在JBoss-Remoting中,只需将需要发起的请求参数对象传入jboss-remoting的InvocationRequest对象即可,也可根据协议基于InvocationRequest封装符合需求的InvocationRequest对象。
3、怎么将请求转化为符合协议的格式的?
JBoss-Remoting基于Java串行化机制或JBoss自己的串行化实现来将请求转化为对象字节流。
4、使用什么传输协议传输?
支持多种传输协议,例如socket、http等。
5、响应端基于什么机制来接收请求?
响应端只需将自己的处理对象注册到JBoss-Remoting提供的server端的Connector对象中即可。
6、怎么将流还原为传输格式的?
JBoss-Remoting基于java串行化机制或jboss自己的串行化实现来将请求信息还原为java对象。
7、处理完毕后怎么回应?
处理完毕后将结果对象直接返回即可,jboss-remoting会将此对象按照协议进行序列化,返回至调用端。
另外,jboss-remoting支持多种通信方式,例如同步/异步/单向通信等。
--------------------------------------------------------------------------------------------------------------------------------------------------
Spring-Remoting
Spring-remoting是Spring提供java领域的远程通讯框架,基于此框架,同样也可以很简单的将普通的spring bean以某种远程协议的方式来发布,同样也可以配置spring bean为远程调用的bean。
1、是基于什么协议实现的?
和JBoss-Remoting一样,作为一个远程通讯的框架,Spring通过集成多种远程通讯的library,从而实现了对多种协议的支持,例如rmi、http+io、xml-rpc、binary-rpc等。
2、怎么发起请求?
在Spring中,由于其对于远程调用的bean采用的是proxy实现,发起请求完全是通过服务接口调用的方式。
3、怎么将请求转化为符合协议的格式的?
Spring按照协议方式将请求的对象信息转化为流,例如Spring Http Invoker是基于Spring自己定义的一个协议来实现的,传输协议上采用的为http,请求信息是基于java串行化机制转化为流进行传输。
4、使用什么传输协议传输?
支持多种传输协议,例如rmi、http等等。
5、响应端基于什么机制来接收请求?
响应端遵循协议方式来接收请求,对于使用者而言,则只需通过spring的配置方式将普通的spring bean配置为响应端或者说提供服务端。
6、怎么将流还原为传输格式的?
按照协议方式来进行还原。
7、处理完毕后怎么回应?
处理完毕后直接返回即可,spring-remoting将根据协议方式来做相应的序列化。
--------------------------------------------------------------------------------------------------------------------------------------------------
Hessian
Hessian是由caucho提供的一个基于binary-RPC实现的远程通讯library。
1、是基于什么协议实现的?
基于Binary-RPC协议实现。
2、怎么发起请求?
需通过Hessian本身提供的API来发起请求。
3、怎么将请求转化为符合协议的格式的?
Hessian通过其自定义的串行化机制将请求信息进行序列化,产生二进制流。
4、使用什么传输协议传输?
Hessian基于Http协议进行传输。
5、响应端基于什么机制来接收请求?
响应端根据Hessian提供的API来接收请求。
6、怎么将流还原为传输格式的?
Hessian根据其私有的串行化机制来将请求信息进行反序列化,传递给使用者时已是相应的请求信息对象了。
7、处理完毕后怎么回应?
处理完毕后直接返回,hessian将结果对象进行序列化,传输至调用端。
--------------------------------------------------------------------------------------------------------------------------------------------------
Burlap
Burlap也是有caucho提供,它和hessian的不同在于,它是基于XML-RPC协议的。
1、是基于什么协议实现的?
基于XML-RPC协议实现。
2、怎么发起请求?
根据Burlap提供的API。
3、怎么将请求转化为符合协议的格式的?
将请求信息转化为符合协议的XML格式,转化为流进行传输。
4、使用什么传输协议传输?
Http协议。
5、响应端基于什么机制来接收请求?
监听Http请求。
6、怎么将流还原为传输格式的?
根据XML-RPC协议进行还原。
7、处理完毕后怎么回应?
返回结果写入XML中,由Burlap返回至调用端。
--------------------------------------------------------------------------------------------------------------------------------------------------
XFire、Axis
XFire、Axis是Webservice的实现框架,WebService可算是一个完整的SOA架构实现标准了,因此采用XFire、Axis这些也就意味着是采用webservice方式了。
1、是基于什么协议实现的?
基于SOAP协议。
2、怎么发起请求?
获取到远端service的proxy后直接调用。
3、怎么将请求转化为符合协议的格式的?
将请求信息转化为遵循SOAP协议的XML格式,由框架转化为流进行传输。
4、使用什么传输协议传输?
Http协议。
5、响应端基于什么机制来接收请求?
监听Http请求。
6、怎么将流还原为传输格式的?
根据SOAP协议进行还原。
7、处理完毕后怎么回应?
返回结果写入XML中,由框架返回至调用端。
--------------------------------------------------------------------------------------------------------------------------------------------------
ActiveMQ
ActiveMQ是JMS的实现,基于JMS这类消息机制实现远程通讯是一种不错的选择,毕竟消息机制本身的功能使得基于它可以很容易的去实现同步/异步/单向调用等,而且消息机制从容错角度上来说也是个不错的选择,这是Erlang能够做到容错的重要基础。
1、是基于什么协议实现的?
基于JMS协议。
2、怎么发起请求?
遵循JMS API发起请求。
3、怎么将请求转化为符合协议的格式的?
不太清楚,猜想应该是二进制流。
4、使用什么传输协议传输?
支持多种传输协议,例如socket、http等等。
5、响应端基于什么机制来接收请求?
监听符合协议的端口。
6、怎么将流还原为传输格式的?
同问题3。
7、处理完毕后怎么回应?
遵循JMS API生成消息,并写入JMS Queue中。
基于JMS此类机制实现远程通讯的例子有Spring-Intergration、Mule、Lingo等等。
--------------------------------------------------------------------------------------------------------------------------------------------------
Mina
Mina是Apache提供的通讯框架,在之前一直没有提到网络IO这块,之前提及的框架或library基本都是基于BIO的,而Mina是采用NIO的,NIO在并发量增长时对比BIO而言会有明显的性能提升,而java性能的提升,与其NIO这块与OS的紧密结合是有不小的关系的。
1、是基于什么协议实现的?
基于纯粹的Socket+NIO。
2、怎么发起请求?
通过Mina提供的Client API。
3、怎么将请求转化为符合协议的格式的?
Mina遵循java串行化机制对请求对象进行序列化。
4、使用什么传输协议传输?
支持多种传输协议,例如socket、http等等。
5、响应端基于什么机制来接收请求?
以NIO的方式监听协议端口。
6、怎么将流还原为传输格式的?
遵循java串行化机制对请求对象进行反序列化。
7、处理完毕后怎么回应?
遵循Mina API进行返回。
MINA是NIO方式的,因此支持异步调用是毫无悬念的。
--------------------------------------------------------------------------------------------------------------------------------------------------
EJB
EJB最突出的在于其分布式,EJB采用的是ORMI协议,和RMI协议是差不多的,但EJB在分布式通讯的安全控制、transport pool、smart proxy等方面的突出使得其在分布式领域是不可忽视的力量。
1、是基于什么协议实现的?
基于ORMI协议。
2、怎么发起请求?
EJB调用。
3、怎么将请求转化为符合协议的格式的?
遵循java串行化机制对请求对象进行序列化。
4、使用什么传输协议传输?
Socket。
5、响应端基于什么机制来接收请求?
监听协议端口。
6、怎么将流还原为传输格式的?
遵循java串行化机制对请求对象进行反序列化。
7、处理完毕后怎么回应?
直接返回处理对象即可。
在之前的分布式服务框架系列的文章中对于jndi有误导的嫌疑,在这篇blog中也顺带的提下jndi的机制,由于JNDI取决于具体的实现,在这里只能是讲解下jboss的jndi的实现了。
在将对象实例绑定到jboss jnp server后,当远程端采用context.lookup()方式获取远程对象实例并开始调用时,jboss jndi的实现方法是从jnp server上获取对象实例,将其序列化回本地,然后在本地进行反序列化,之后在本地进行类调用。
通过这个机制,就可以知道了,本地其实是必须有绑定到jboss上的对象实例的class的,否则反序列化的时候肯定就失败了,而远程通讯需要做到的是在远程执行某动作,并获取到相应的结果,可见纯粹基于JNDI是无法实现远程通讯的。
但JNDI也是实现分布式服务框架一个很关键的技术点,因为可以通过它来实现透明化的远端和本地调用,就像ejb,另外它也是个很好的隐藏实际部署机制(就像datasource)等的方案。
总结
由上一系列的分析可知,在远程通讯领域中,涉及的知识点还是相当的多的,例如有:通信协议(Socket/tcp/http/udp/rmi/xml-rpc etc.)、消息机制、网络IO(BIO/NIO/AIO)、MultiThread、本地调用与远程调用的透明化方案(涉及java classloader、Dynamic Proxy、Unit Test etc.)、异步与同步调用、网络通信处理机制(自动重连、广播、异常、池处理等等)、Java Serialization (各种协议的私有序列化机制等)、各种框架的实现原理(传输格式、如何将传输格式转化为流的、如何将请求信息转化为传输格式的、如何接收流的、如何将流还原为传输格式的等等),要精通其中的哪些东西,得根据实际需求来决定了,只有在了解了原理的情况下才能很容易的做出选择,甚至可以根据需求做私有的远程通讯协议,对于从事分布式服务平台或开发较大型的分布式应用的人而言,我觉得至少上面提及的知识点是需要比较了解的。
参考文档(感谢这些文章)
RMI原理及实现:http://www.yesky.com/274/1625274.shtml
Java NIO原理和使用:http://www.jdon.com/concurrent/nio%D4%AD%C0%ED%D3%A6%D3%C3.htm
XML RPC协议:http://hedong.3322.org/archives/000470.html
http://www.mengyan.org/blog/archives/2005/07/12/30.html
Spring技术应用中的远程服务详解:http://www.builder.com.cn/2007/1027/583384.shtml
JAVA RPC通信机制之SOAP:http://www.java114.com/content16/content3826.html
Java Remoting:Protocol BenchMarks:http://q.sohu.com/forum/5/topic/1148909
Evalution of RMI Alternative:https://www.jfire.org/modules/phpwiki/index.php/Evaluation%20of%20RMI%20Alternative
Metaprotocol Taxonomy:http://hessian.caucho.com/doc/metaprotocol-taxonomy.xtp
什么是Webservice:http://www.vchome.net/dotnet/webservice/webservice15.htm
14:20 浏览 (227) 评论 (0) 分类: MINA 2008-07-22
缩略显示MINA Begin
1. 传统Socket:阻塞式通信
在java传统socket技术中,每建立一个Socket连接时,须同时创建一个新线程对该Socket进行单独通信(采用阻塞的方式通信)。
这种方式具有很高的响应速度,并且控制起来也很简单,在连接数较少的时候非常有效,但是如果对每一个连接都产生一个线程无疑是对系统资源的一种浪费,如果连接数较多将会出现资源不足的情况。下面的代码就说明了这一点。
a) server code:
package Socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class MultiUserServer extends Thread {
private Socket client;
public MultiUserServer(Socket c) {
this.client = c;
}
public void run() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
// Mutil User but can’t parallel
while (true) {
String str = in.readLine();
System.out.println("receive message: " + str);
if (str.equals("end")) break;
}
client.close();
} catch (IOException ex) {
}
}
public static void main(String[] args) throws IOException {
int port = 10086;
if (args.length > 0)
port = Integer.parseInt(args[0]);
ServerSocket server = new ServerSocket(port);
System.out.println("the server socket application is created!");
while (true) {
// transfer location change Single User or Multi User
MultiUserServer mu = new MultiUserServer(server.accept());
mu.start();
}
}
}
b) client code:
package Socket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Client {
static Socket server;
public static void main(String[] args) throws Exception {
String host = "192.168.0. 10";
int port = 10086;
if (args.length > 1) {
host = args[0];
port = Integer.parseInt(args[1]);
}
System.out.println("connetioning:" + host + ":" + port);
server = new Socket(host, port);
PrintWriter out = new PrintWriter(server.getOutputStream());
BufferedReader wt = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String str = wt.readLine();
out.println(str);
out.flush();
if (str.equals("end")) break;
}
server.close();
}
}
2. nio socket: 非阻塞通讯模式
a) NIO 设计背后的基石:反应器模式
反应器模式: 用于事件多路分离和分派的体系结构模式。
反应器模式的核心功能如下:
将事件多路分用
将事件分派到各自相应的事件处理程序
b) NIO 的非阻塞 I/O 机制是围绕 选择器和 通道构建的。
选择器(Selector类): 是 Channel 的多路复用器。Selector 类将传入客户机请求多路分用并将它们分派到各自的请求处理程序。
通道(Channel 类):表示服务器和客户机之间的一种通信机制,一个通道负责处理一类请求/事件。
简单的来说:
NIO是一个基于事件的IO架构,最基本的思想就是:有事件我会通知你,你再去做与此事件相关的事情。而且NIO的主线程只有一个,不像传统的模型,需要多个线程以应对客户端请求,也减轻了JVM的工作量。
c) 当Channel注册至Selector以后,经典的调用方法如下:
while (somecondition) {
int n = selector.select(TIMEOUT);
if (n == 0) continue;
for (Iterator iter = selector.selectedKeys().iterator(); iter.hasNext();) {
if (key.isAcceptable()) doAcceptable(key);
if (key.isConnectable()) doConnectable(key);
if (key.isValid() && key.isReadable()) doReadable(key);
if (key.isValid() && key.isWritable()) doWritable(key);
iter.remove();
}
}
NIO 有一个主要的类Selector,这个类似一个观察者,只要我们把需要探知的socketchannel告诉Selector,我们接着做别的事情,当有事件发生时,他会通知我们,传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的socketchannel,然后,我们从这个Channel中读取数据,放心,包准能够读到,接着我们可以处理这些数据。
Selector内部原理实际是在做一个对所注册的channel的轮询访问,不断的轮询(目前就这一个算法),一旦轮询到一个channel有所注册的事情发生,比如数据来了,他就会站起来报告,交出一把钥匙,让我们通过这把钥匙来读取这个channel的内容。
d) Sample01
package NIO;
// 程序目的:学习Java NIO#SocketChannel
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class SocketChannelDemo {
public static int PORT_NUMBER = 23;// 监听端口
static String line = "";
ServerSocketChannel serverChannel;
ServerSocket serverSocket;
Selector selector;
private ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
public static void main(String[] args) throws Exception {
SocketChannelDemo server = new SocketChannelDemo();
server.init(args);
server.startWork();
}
public void init(String[] argv) throws Exception {
int port = PORT_NUMBER;
if (argv.length > 0) port = Integer.parseInt(argv[0]);
System.out.println("Listening on port " + port);
// 分配一个ServerSocketChannel
serverChannel = ServerSocketChannel.open();
// 从ServerSocketChannel里获得一个对应的Socket
serverSocket = serverChannel.socket();
// 生成一个Selector
selector = Selector.open();
// 把Socket绑定到端口上
serverSocket.bind(new InetSocketAddress(port));
// serverChannel为非bolck
serverChannel.configureBlocking(false);
// 通过Selector注册ServerSocetChannel
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}
public void startWork() throws Exception {
while (true) {
int n = selector.select();// 获得IO准备就绪的channel数量
if (n == 0) continue; // 没有channel准备就绪,继续执行
// 用一个iterator返回Selector的selectedkeys
Iterator it = selector.selectedKeys().iterator();
// 处理每一个SelectionKey
while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
// 判断是否有新的连接到达
if (key.isAcceptable()) {
// 返回SelectionKey的ServerSocketChannel
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel channel = server.accept();
registerChannel(selector, channel, SelectionKey.OP_READ);
doWork(channel);
}
// 判断是否有数据在此channel里需要读取
if (key.isReadable()) processData(key);
// 删除 selectedkeys
it.remove();
}
}
}
protected void registerChannel(Selector selector, SelectableChannel channel, int ops) throws Exception {
if (channel == null) return;
channel.configureBlocking(false);
channel.register(selector, ops);
}
// 处理接收的数据
protected void processData(SelectionKey key) throws Exception {
SocketChannel socketChannel = (SocketChannel) key.channel();
int count;
buffer.clear(); // 清空buffer
// 读取所有的数据
while ((count = socketChannel.read(buffer)) > 0) {
buffer.flip();
// send the data, don′t assume it goes all at once
while (buffer.hasRemaining()) {
char c = (char) buffer.get();
line += c;
// 如果收到回车键,则在返回的字符前增加[echo]$字样,并且server端打印出字符串
if (c == (char) 13) {
buffer.clear();
buffer.put("[echo]$".getBytes());
buffer.flip();
System.out.println(line); //
line = "";
}
socketChannel.write(buffer);// 在Socket里写数据
}
buffer.clear(); // 清空buffer
}
if (count < 0) socketChannel.close(); // count<0,说明已经读取完毕
}
private void doWork(SocketChannel channel) throws Exception {
buffer.clear();
buffer.put("Hello,I am working,please input some thing,and i will echo to you![echo]$".getBytes());
buffer.flip();
channel.write(buffer);
}
}
运行此程序,然后在控制台输入命令telnet localhost 23。
e) Server code:
public class NonBlockingServer {
public Selector sel = null;
public ServerSocketChannel server = null;
public SocketChannel socket = null;
public int port = 4900;
String result = null;
public NonBlockingServer() {
System.out.println("Inside default ctor");
}
public NonBlockingServer(int port) {
System.out.println("Inside the other ctor");
this.port = port;
}
public void initializeOperations() throws IOException,UnknownHostException {
System.out.println("Inside initialization");
sel = Selector.open();
server = ServerSocketChannel.open();
server.configureBlocking(false);
InetAddress ia = InetAddress.getLocalHost();
InetSocketAddress isa = new InetSocketAddress(ia,port);
server.socket().bind(isa);
}
public void startServer() throws IOException {
System.out.println("Inside startserver");
initializeOperations();
System.out.println("Abt to block on select()");
SelectionKey acceptKey = server.register(sel, SelectionKey.OP_ACCEPT );
while (acceptKey.selector().select() > 0 ) {
Set readyKeys = sel.selectedKeys();
Iterator it = readyKeys.iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey)it.next();
it.remove();
if (key.isAcceptable()) {
System.out.println("Key is Acceptable");
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
socket = (SocketChannel) ssc.accept();
socket.configureBlocking(false);
SelectionKey another = socket.register(sel,SelectionKey.OP_READ|SelectionKey.OP_WRITE);
}
if (key.isReadable()) {
System.out.println("Key is readable");
String ret = readMessage(key);
if (ret.length() > 0) writeMessage(socket,ret);
}
if (key.isWritable()) {
System.out.println("THe key is writable");
String ret = readMessage(key);
socket = (SocketChannel)key.channel();
if (result.length() > 0 ) writeMessage(socket,ret);
}
}
}
}
public void writeMessage(SocketChannel socket,String ret) {
System.out.println("Inside the loop");
if (ret.equals("quit") || ret.equals("shutdown")) return;
try {
String s = "This is content from server!—————————————–";
Charset set = Charset.forName("us-ascii");
CharsetDecoder dec = set.newDecoder();
CharBuffer charBuf = dec.decode(ByteBuffer.wrap(s.getBytes()));
System.out.println(charBuf.toString());
int nBytes = socket.write(ByteBuffer.wrap((charBuf.toString()).getBytes()));
System.out.println("nBytes = "+nBytes);
result = null;
} catch(Exception e) {
e.printStackTrace();
}
}
public String readMessage(SelectionKey key) {
int nBytes = 0;
socket = (SocketChannel)key.channel();
ByteBuffer buf = ByteBuffer.allocate(1024);
try {
nBytes = socket.read(buf);
buf.flip();
Charset charset = Charset.forName("us-ascii");
CharsetDecoder decoder = charset.newDecoder();
CharBuffer charBuffer = decoder.decode(buf);
result = charBuffer.toString();
} catch(IOException e) {
e.printStackTrace();
}
return result;
}
public static void main(String args[]) {
NonBlockingServer nb;
if (args.length < 1) nb = new NonBlockingServer()
else {
int port = Integer.parseInt(args[0]);
nb = new NonBlockingServer(port);
}
try {
nb.startServer();
System.out.println("the nonBlocking server is started!");
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
}
2.2.4.2 Client code:
public class Client {
public SocketChannel client = null;
public InetSocketAddress isa = null;
public RecvThread rt = null;
private String host;
private int port;
public Client(String host, int port) {
this.host = host;
this.port = port;
}
public void makeConnection() {
String proxyHost = "192.168.254.212";
String proxyPort = "1080";
System.getProperties().put("socksProxySet", "true");
System.getProperties().put("socksProxyHost", proxyHost);
System.getProperties().put("socksProxyPort", proxyPort);
int result = 0;
try {
client = SocketChannel.open();
isa = new InetSocketAddress(host, port);
client.connect(isa);
client.configureBlocking(false);
receiveMessage();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
long begin = System.currentTimeMillis();
sendMessage();
long end = System.currentTimeMillis();
long userTime = end - begin;
System.out.println("use tiem: " + userTime);
try {
interruptThread();
client.close();
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
}
public int sendMessage() {
System.out.println("Inside SendMessage");
String msg = null;
ByteBuffer bytebuf;
int nBytes = 0;
try {
msg = "It’s message from client!";
System.out.println("msg is "+msg);
bytebuf = ByteBuffer.wrap(msg.getBytes());
for (int i = 0; i < 1000; i++) {
nBytes = client.write(bytebuf);
System.out.println(i + " finished");
}
interruptThread();
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
client.close();
return -1;
} catch (IOException e) {
e.printStackTrace();
}
return nBytes;
}
public void receiveMessage() {
rt = new RecvThread("Receive THread", client);
rt.start();
}
public void interruptThread() {
rt.val = false;
}
public static void main(String args[]) {
if (args.length < 2) {
System.err.println("You should put 2 args: host,port");
} else {
String host = args[0];
int port = Integer.parseInt(args[1]);
Client cl = new Client(host, port);
cl.makeConnection();
}
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String msg;
}
public class RecvThread extends Thread {
public SocketChannel sc = null;
public boolean val = true;
public RecvThread(String str, SocketChannel client) {
super(str);
sc = client;
}
public void run() {
int nBytes = 0;
ByteBuffer buf = ByteBuffer.allocate(2048);
try {
while (val) {
while ((nBytes = nBytes = client.read(buf)) > 0) {
buf.flip();
Charset charset = Charset.forName("us-ascii");
CharsetDecoder decoder = charset.newDecoder();
CharBuffer charBuffer = decoder.decode(buf);
String result = charBuffer.toString();
System.out.println("the server return: " + result);
buf.flip();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Reactor模式和NIO
当前分布式计算 Web Services盛行天下,这些网络服务的底层都离不开对socket的操作。他们都有一个共同的结构:
Read request
Decode request
Process service
Encode reply
Send reply
经典的网络服务的设计如下图,在每个线程中完成对数据的处理:
但这种模式在用户负载增加时,性能将下降非常的快。我们需要重新寻找一个新的方案,保持数据处理的流畅,很显然,事件触发机制是最好的解决办法,当有事件发生时,会触动handler,然后开始数据的处理。
Reactor模式类似于AWT中的Event处理:
Reactor模式参与者
1.Reactor 负责响应IO事件,一旦发生,广播发送给相应的Handler去处理,这类似于AWT的thread
2.Handler 是负责非堵塞行为,类似于AWT ActionListeners;同时负责将handlers与event事件绑定,类似于AWT addActionListener
如图:
Java的NIO为reactor模式提供了实现的基础机制,它的Selector当发现某个channel有数据时,会通过SlectorKey来告知我们,在此我们实现事件和handler的绑定。
我们来看看Reactor模式代码:
public class Reactor implements Runnable{
final Selector selector;
final ServerSocketChannel serverSocket;
Reactor(int port) throws IOException {
selector = Selector.open();
serverSocket = ServerSocketChannel.open();
InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(),port);
serverSocket.socket().bind(address);
serverSocket.configureBlocking(false);
//向selector注册该channel
SelectionKey sk =serverSocket.register(selector,SelectionKey.OP_ACCEPT);
logger.debug("–>Start serverSocket.register!");
//利用sk的attache功能绑定Acceptor 如果有事情,触发Acceptor
sk.attach(new Acceptor());
logger.debug("–>attach(new Acceptor()!");
}
public void run() { // normally in a new Thread
try {
while (!Thread.interrupted())
{
selector.select();
Set selected = selector.selectedKeys();
Iterator it = selected.iterator();
//Selector如果发现channel有OP_ACCEPT或READ事件发生,下列遍历就会进行。
while (it.hasNext())
//来一个事件第一次触发一个accepter线程
//以后触发SocketReadHandler
dispatch((SelectionKey)(it.next()));
selected.clear();
}
}catch (IOException ex) {
logger.debug("reactor stop!"+ex);
}
}
//运行Acceptor或SocketReadHandler
void dispatch(SelectionKey k) {
Runnable r = (Runnable)(k.attachment());
if (r != null){
// r.run();
}
}
class Acceptor implements Runnable { // inner
public void run() {
try {
logger.debug("–>ready for accept!");
SocketChannel c = serverSocket.accept();
if (c != null)
//调用Handler来处理channel
new SocketReadHandler(selector, c);
}
catch(IOException ex) {
logger.debug("accept stop!"+ex);
}
}
}
}
以上代码中巧妙使用了SocketChannel的attach功能,将Hanlder和可能会发生事件的channel链接在一起,当发生事件时,可以立即触发相应链接的Handler。
再看看Handler代码:
public class SocketReadHandler implements Runnable {
public static Logger logger = Logger.getLogger(SocketReadHandler.class);
private Test test=new Test();
final SocketChannel socket;
final SelectionKey sk;
static final int READING = 0, SENDING = 1;
int state = READING;
public SocketReadHandler(Selector sel, SocketChannel c)
throws IOException {
socket = c;
socket.configureBlocking(false);
sk = socket.register(sel, 0);
//将SelectionKey绑定为本Handler 下一步有事件触发时,将调用本类的run方法。
//参看dispatch(SelectionKey k)
sk.attach(this);
//同时将SelectionKey标记为可读,以便读取。
sk.interestOps(SelectionKey.OP_READ);
sel.wakeup();
}
public void run() {
try{
// test.read(socket,input);
readRequest() ;
}catch(Exception ex){
logger.debug("readRequest error"+ex);
}
}
private void readRequest() throws Exception {
ByteBuffer input = ByteBuffer.allocate(1024);
input.clear();
try {
int bytesRead = socket.read(input);
……
//激活线程池处理这些request
requestHandle(new Request(socket,btt));
…..
}catch(Exception e) {
}
}
注意在Handler里面又执行了一次attach,这样,覆盖前面的Acceptor,下次该Handler又有READ事件发生时,将直接触发Handler.从而开始了数据的读 处理 写 发出等流程处理。
将数据读出后,可以将这些数据处理线程做成一个线程池,这样,数据读出后,立即扔到线程池中,这样加速处理速度:
更进一步,我们可以使用多个Selector分别处理连接和读事件。
一个高性能的Java网络服务机制就要形成,激动人心的集群并行计算即将实现。
3. Socket网络框架 MINA
a) Overview
MINA是一个网络应用框架,在不牺牲性能和可扩展性的前提下用于解决如下问题:
快速开发自己的应用。
高可维护性,高可复用性:网络I/O编码,消息的编/解码,业务逻辑互相分离。
相对容易的进行单元测试。
b) MINA架构:
IoSessionManager: Where real I/O occurs
IoFilters: Filters I/O events • requests
IoHandler: Your protocol logic
IoSession: Represents a connection
IoFilters:
IoFilter为MINA的功能扩展提供了接口。它拦截所有的IO事件进行事件的预处理和河畜处理(AOP)。我们可以把它想象成Servlet的filters。
IoFilter能够实现以下几种目的:
事件日志
性能检测
数据转换(e.g. SSL support),codec
防火墙…等等
codec: ProtocolCodecFactory
MINA提供了方便的Protocol支持。如上说讲,codec在IoFilters中设置。
通过它的Encoder和Decoder,可以方便的扩展并支持各种基于Socket的网络协议,比如HTTP服务器、FTP服务器、Telnet服务器等等。
要实现自己的编码/解码器(codec)只需要实现interface: ProtocolCodecFactory即可.
在MINA 1.0版本,MINA已经实现了几个常用的(codec factory):
DemuxingProtocolCodecFactory,
NettyCodecFactory,
ObjectSerializationCodecFactory,
TextLineCodecFactory
其中:
TextLineCodecFactory:
A ProtocolCodecFactory that performs encoding and decoding between a text line data and a Java
string object. This codec is useful especially when you work with a text-based protocols such as SMTP and IMAP.
ObjectSerializationCodecFactory:
A ProtocolCodecFactory that serializes and deserializes Java objects. This codec is very useful when
you have to prototype your application rapidly without any specific codec.
DemuxingProtocolCodecFactory:
A composite ProtocolCodecFactory that consists of multiple MessageEncoders and MessageDecoders. ProtocolEncoder and ProtocolDecoder this factory returns demultiplex incoming messages and buffers to appropriate MessageEncoders and MessageDecoders.
NettyCodecFactory:
A MINA ProtocolCodecFactory that provides encoder and decoder for Netty2 Messages and MessageRecognizers.
IoHandler :business logic
MINA中,所有的业务逻辑都在实现了IoHandler的class完成。
Interface Handle:
all protocol events fired by MINA. There are 6 event handler methods, and they are all invoked by MINA automatically.
当事件发生时,将触发IoHandler中的方法:
sessionCreated:当一个session创建的时候调用;
sessionOpened:在sessionCreated调用之后被调用;
sessionClosed:当IO连接被关闭时被调用;
sessionIdle:当在远程实体和用户程序之间没有数据传输的时候被调用;
exceptionCaught:当IoAcceptor 或者IoHandler.中出现异常时被调用;
messageReceived:当接受到消息时调用;
messageSent:当发出请求时调用。
MINA 1.0中,IoHandler的实现类:
ChainedIoHandler
DemuxingIoHandler,
IoHandlerAdapter
SingleSessionIoHandlerDelegate
StreamIoHandler
具体细节可参考javadoc。
c) MINA的高级主题:线程模式
MINA通过它灵活的filter机制来提供多种线程模型。
没有线程池过滤器被使用时MINA运行在一个单线程模式。
如果添加了一个IoThreadPoolFilter到IoAcceptor,将得到一个leader-follower模式的线程池。
如果再添加一个ProtocolThreadPoolFilter,server将有两个线程池:
一个(IoThreadPoolFilter)被用于对message对象进行转换,另外一个(ProtocolThreadPoolFilter)被用于处理业务逻辑。
SimpleServiceRegistry加上IoThreadPoolFilter和ProtocolThreadPoolFilter的缺省实现即可适用于需要高伸缩性的应用。如果想使用自己的线程模型,请参考SimpleServiceRegistry的源代码,并且自己初始化Acceptor。
IoThreadPoolFilter threadPool = new IoThreadPoolFilter();threadPool.start();
IoAcceptor acceptor = new SocketAcceptor();
acceptor.getFilterChain().addLast( "threadPool", threadPool);
ProtocolThreadPoolFilter threadPool2 = new ProtocolThreadPoolFilter();
threadPool2.start();
ProtocolAcceptor acceptor2 = new IoProtocolAcceptor( acceptor );
acceptor2.getFilterChain().addLast( "threadPool", threadPool2 );
…
threadPool2.stop();
threadPool.stop();
d) 采用MINA进行socket开发,一般步骤如下:
Begin:
IoAcceptor acceptor = new SocketAcceptor(); //建立client接收器
or client:
SocketConnector connector = new SocketConnector(); //建立一个连接器
server的属性配置:
SocketAcceptorConfig cfg = new SocketAcceptorConfig();
cfg.setReuseAddress(true);
cfg.getFilterChain().addLast( "codec",
new ProtocolCodecFilter( new ObjectSerializationCodecFactory() ) ); //对象序列化 codec factory
cfg.getFilterChain().addLast( "logger", new LoggingFilter() );
绑定address和business logic
server:
acceptor.bind(new InetSocketAddress( SERVER_PORT ),
new ServerSessionHandler( ), cfg ); // 绑定address和handler
client:
connector.connect(new InetSocketAddress( HOSTNAME, PORT ),
new ClientSessionHandler(msg), cfg );
实现自己的业务逻辑: IoHandler
如有必要,实现自己的CODEC
下面的代码演示了采用ObjectSerializationCodecFactory给服务端传送文件:
e) Client
public class Client {
private static final String HOSTNAME = "192.168.0.81";
private static final int PORT = 8080;
private static final int CONNECT_TIMEOUT = 30; // seconds
public static void main( String[] args ) throws Throwable {
System.out.println("in nio client");
SocketConnector connector = new SocketConnector();
// Configure the service.
SocketConnectorConfig cfg = new SocketConnectorConfig();
cfg.setConnectTimeout( CONNECT_TIMEOUT );
cfg.getFilterChain().addLast(
"codec",
new ProtocolCodecFilter( new ObjectSerializationCodecFactory() ) );
cfg.getFilterChain().addLast( "logger", new LoggingFilter() );
IoSession session;
if(args.length > 1) {
connector.connect(new InetSocketAddress( HOSTNAME, PORT ),
new ClientSessionHandler(args), cfg );
} else {
String[] files = {"E:/music/lcl/juhuatai.mp3",
"E:/music/lcl/jimosazhouleng.mp3"};
connector.connect(new InetSocketAddress( HOSTNAME, PORT ),
new ClientSessionHandler(files), cfg );
}
}
}
f) Clint handle(client端的业务代码)
public class ClientSessionHandler extends IoHandlerAdapter {
private String[] files;
public ClientSessionHandler(String[] files) {
this.files = files;
}
public void sessionOpened( IoSession session ) {
for (int i = 0; i < this.files.length; i++) {
Thread sendMessageThread = new SendMessageThread("Thread" + i, session,files[i]);
sendMessageThread.start();
}
}
public void messageReceived( IoSession session, Object message ) {
System.out.println("in messageReceived!");
}
public void exceptionCaught( IoSession session, Throwable cause ) {
session.close();
}
public class SendMessageThread extends Thread {
private IoSession session;
private String filename;
public SendMessageThread(String name, IoSession session, String filename) {
super(name);
this.session = session;
this.filename = filename;
}
public void run() {
System.out.println("start thread: " + this.getName());
try {
ByteBuffer buf = ByteBuffer.allocate(Constants.BUF_SIZE);
FileChannel fc = new FileInputStream(filename).getChannel();
int index;
while ((index = NioFileUtil.readFile(fc, buf)) > 0) {
buf.flip();
byte[] bs;
if (index == buf.capacity()) {
bs = buf.array();
} else {
bs = new byte[index];
int i = 0;
while (buf.hasRemaining()) {
bs[i++] = buf.get();
}
}
Message msg = new Message(filename,Constants.CMD_SEND, bs);
session.write(msg);
}
Message msg = new Message(filename, Constants.CMD_FINISHED, null);
session.write(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
g) Server:
public class Server {
private static final int SERVER_PORT = 8080;
public static void main( String[] args ) throws Throwable {
IoAcceptor acceptor = new SocketAcceptor();
// Prepare the service configuration.
SocketAcceptorConfig cfg = new SocketAcceptorConfig();
cfg.setReuseAddress( true );
cfg.getFilterChain().addLast(
"codec",
new ProtocolCodecFilter( new ObjectSerializationCodecFactory() ) );
cfg.getFilterChain().addLast( "logger", new LoggingFilter() );
acceptor.bind(
new InetSocketAddress( SERVER_PORT ),
new ServerSessionHandler( ), cfg );
System.out.println( "nioFileServer Listening on port " + SERVER_PORT );
}
}
h) Server handle:(Server端业务代码)
public class ServerSessionHandler extends IoHandlerAdapter {
public void sessionOpened( IoSession session ) {
// set idle time to 60 seconds
System.out.println("in sessionOpened");
session.setIdleTime( IdleStatus.BOTH_IDLE, 60 );
session.setAttribute("times",new Integer(0));
}
public void messageReceived( IoSession session, Object message ) {
System.out.println("in messageReceived");
Message msg = (Message) message;
System.out.println("the file name is: " + msg.getFileName() + ""n");
this.process(session, msg);
}
private void process(IoSession session, Message message) {
String[] temparray = message.getFileName().split("[//]");
String filename ="d:/" + temparray[temparray.length - 1];
if (session.containsAttribute(message.getFileName())) {
FileChannel channel = (FileChannel)session.getAttribute(message.getFileName());
if (message.getType().equals(Constants.CMD_SEND)) {
try {
NioFileUtil.writeFile(channel, ByteBuffer.wrap(message.getContent()));
} catch (Exception e) {
e.printStackTrace();
}
} else {
try {
channel.close();
channel = null;
session.removeAttribute(message.getFileName());
} catch (IOException e) {
e.printStackTrace();
}
}
} else {
try {
FileChannel channel = new FileOutputStream(filename).getChannel();
NioFileUtil.writeFile(channel, ByteBuffer.wrap(message.getContent()));
session.setAttribute(message.getFileName(), channel);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void sessionIdle( IoSession session, IdleStatus status ) {
SessionLog.info( session, "Disconnecting the idle." );
// disconnect an idle client
session.close();
}
public void exceptionCaught( IoSession session, Throwable cause ) {
// close the connection on exceptional situation
session.close();
}
}
i) 文件操作:
public class NioFileUtil {
public static void writeFile(FileChannel fileChannel, ByteBuffer buf) throws Exception {
buf.clear();
fileChannel.write(buf);
}
public static int readFile(FileChannel fileChannel,ByteBuffer buf) throws IOException {
buf.rewind();
int index = fileChannel.read(buf);
return index;
}
}
j) 常量:
public class Constants {
public static final String CMD_FINISHED = "FINISHED";
public static final String CMD_SEND = "SEND";
public static final int BUF_SIZE = 10240;
private Constants(){}
}
Demo
Introduction
org.apache.mina.example.chat
Chat server which demonstates using the text line codec and Spring integration.
org.apache.mina.example.chat.client
Swing based chat client.
org.apache.mina.example.echoserver
Echo server which demonstate
10:59 浏览 (1335) 评论 (2) 分类: MINA 2008-07-21
缩略显示Spring,smppapi,apache mina, ssl快速实现安全的smpp(6)
接上一篇: http://618119.com/archives/2007/12/13/45.html
使用 commons ssl生成 SSLContext :
view plaincopy to clipboardprint?
package com.lizongbo.ssl;
import javax.net.ssl.SSLContext;
import java.security.GeneralSecurityException;
import java.io.IOException;
import javax.net.ssl.KeyManager;
import org.apache.commons.ssl.KeyMaterial;
public class SMPPSSLContextFactory {
private static final String PROTOCOL = “TLS”;
private static final String CA_FILE = “ca.crt.properties”;
private static final String CERT_FILE = “server.crt.properties”;
private static final String KEY_FILE = “server.key.properties”;
private static final String CILENT_FILE = “client.crt.properties”;;//”client.p12.properties”;
private static final String CILENT_KEY_FILE = “client.key.properties”;
private static final char[] password =new char[0] ;//”lizongbo”.toCharArray();
private static SSLContext serverInstance = null;
private static SSLContext clientInstance = null;
/**
* Get SSLContext singleton.
*
* @return SSLContext
* @throws java.security.GeneralSecurityException
*
*/
public static SSLContext getInstance(boolean server) throws
GeneralSecurityException, IOException {
SSLContext retInstance = null;
if (server) {
if (serverInstance == null) {
synchronized (SMPPSSLContextFactory.class) {
if (serverInstance == null) {
try {
serverInstance = createSMPPServerSSLContext();
}
catch (Exception ioe) {
throw new GeneralSecurityException(
“Can’t create Server SSLContext:” + ioe);
}
}
}
}
retInstance = serverInstance;
}
else {
if (clientInstance == null) {
synchronized (SMPPSSLContextFactory.class) {
if (clientInstance == null) {
clientInstance = createSMPPClientSSLContext();
}
}
}
retInstance = clientInstance;
}
return retInstance;
}
private static SSLContext createSMPPServerSSLContext() throws
GeneralSecurityException, IOException {
// ssl.setCheckHostname(false); // default setting is “false” for SSLServer
// ssl.setCheckExpiry(true); // default setting is “true” for SSLServer
// ssl.setCheckCRL(true); // default setting is “true” for SSLServer
// ssl.useStrongCiphers();
// return ssl.getSSLContext();
SSLContext sslContext = SSLContext.getInstance(PROTOCOL);
KeyMaterial km = new KeyMaterial(SMPPSSLContextFactory.class
.getResourceAsStream(CERT_FILE),
SMPPSSLContextFactory.class
.getResourceAsStream(KEY_FILE),
password);
sslContext.init( (KeyManager[]) km.getKeyManagers(),
SMPPTrustManagerFactory.X509_MANAGERS, null);
// System.out.println(”getCipherSuites ==” +
// java.util.Arrays.toString(sslContext.getServerSessionContext().
// getSupportedSSLParameters().
// getCipherSuites()));
return sslContext;
}
private static SSLContext createSMPPClientSSLContext() throws
GeneralSecurityException, IOException {
{
SSLContext context = SSLContext.getInstance(PROTOCOL);
KeyMaterial km = new KeyMaterial(SMPPSSLContextFactory.class
.getResourceAsStream(CILENT_FILE),
SMPPSSLContextFactory.class
.getResourceAsStream(CILENT_KEY_FILE),
password);
context.init( (KeyManager[]) km.getKeyManagers(),
SMPPTrustManagerFactory.X509_MANAGERS, null);
return context;
}
}
}
package com.lizongbo.ssl;
import javax.net.ssl.SSLContext;
import java.security.GeneralSecurityException;
import java.io.IOException;
import javax.net.ssl.KeyManager;
import org.apache.commons.ssl.KeyMaterial;
public class SMPPSSLContextFactory {
private static final String PROTOCOL = “TLS”;
private static final String CA_FILE = “ca.crt.properties”;
private static final String CERT_FILE = “server.crt.properties”;
private static final String KEY_FILE = “server.key.properties”;
private static final String CILENT_FILE = “client.crt.properties”;;//”client.p12.properties”;
private static final String CILENT_KEY_FILE = “client.key.properties”;
private static final char[] password =new char[0] ;//”lizongbo”.toCharArray();
private static SSLContext serverInstance = null;
private static SSLContext clientInstance = null;
/**
* Get SSLContext singleton.
*
* @return SSLContext
* @throws java.security.GeneralSecurityException
*
*/
public static SSLContext getInstance(boolean server) throws
GeneralSecurityException, IOException {
SSLContext retInstance = null;
if (server) {
if (serverInstance == null) {
synchronized (SMPPSSLContextFactory.class) {
if (serverInstance == null) {
try {
serv