flex java通信协议(收藏共享)
flex java通信协议
如何将 Flex 和 Spring 进行集成,使 Flex 前端能够与 Java EE 后端进行通信? Flex 通过远程方法调用和实时通信技术实现异步通信,Flex 的通信协议主要有三种:HttpService、WebService 和 RemoteObject。RomoteObject 协议作为 Flex 提供的最快的通信方式,通过集成 BlazeDS,利用 AMF(Action Message Format)二进制协议使得 Flex 前端能轻松与 Java EE 后端进行数据交互,它是 Flex 集成 Spring 的首选通信协议。
BlazeDS 是 Adobe Live-Cycle Service 的免费开源版本,它使用 AMF 二进制协议通过 AMF 管道构建了 Flex 和 Spring 进行数据通信的桥梁。BlazeDS 可以实现 Flex 对 Java 对象的远程调用。BlazeDS 可以部署运行在大多数 Web 应用服务器上,如 Tomcat、Websphere、JBoss 以及 Weblogic。
提到通信就得面临两个问题,一是通信协议的选择,二是数据协议的定义。通信协议耳熟能详的就有好几种,TCP,UDP,HTTP,FTP等等。数据协议是一种数据交换的格式,像jason,xml,amf3,google protocol都可以用作数据协议,你也可以自己根据通信的效率,安全等因素来定义自己的数据协议。
通信系统的开发是一项很复杂的工作,不要以为往发服务端发一个Hello World!就认为完全掌握了通信系统的开发。概括来说要开发一个健壮的通信系统,必须从这几个方面来着手。
一,通信粘包的处理
这里包的概念是逻辑上的数据包,也就是我们发送的一个完整业务消息包,粘包情况有两种,一种是粘在一起的包都是完整的数据包,另一种情况是粘在一起的包有不完整的包。不是所有的粘包现象都需要处理,若传输的数据为不带结构的连续流数据(如文件传输),则不必把粘连的包分开(简称分包)。但在实际工程应用中,传输的数据一般为带结构的数据,这时就需要做分包处理。
为了避免粘包现象,可采取以下几种措施。一是对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令 push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;二是对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象;三是由接收方控制,将一包数据按结构字段,人为控制分多次接收,然后合并,通过这种手段来避免粘包。
以上提到的三种措施,都有其不足之处。总的来说降低了通信系统的吞吐量。我们可以自己设计一个分包算法来处理粘包的问题,该算法的实现是这样的:
1. 当有数据到达时,将数据压入程序缓冲区。
2. 循环处理缓冲区,如果缓冲区长度大于包头长度,则取出长度信息n,否则跳出循环,如果缓冲区的长度大于n,则从缓冲区取出一个完整包进行处理,否则跳出循环。
如果你是Java的爱好都可以参考一下Mina和netty2的实现,像Mina和Netty2都提供了粘包处理类可供使用,像Mina的CumulativeProtocolDecoder类,Netty2的LengthFieldBasedFrameDecoder。
二,数据协议选择
现在已经有很多数据协议可供我们选择,像jason,xml,amf3,google protocol等等,这些协议相应的语言都有API来对自身数据做协议处理,我们选择协标准无非就是效率和大小,这里每个人可以根据实际的应用环境选择适合的数据协议。
三,网络系统的安全性
网络安全是一个永远的话题,对通信数据加密一般常RSA对byte流加密,FLOOD验证,IP黑名单验证都是必须考虑到的。
以上是做网络开发必须了解的一些基础知识,在这里我们使用一个具体的实例来加深一下理解,Java与Flex使用AMF3数据协议通信。做过网络开发的一般都会知道套接字(SOCKET),很多语言都会通SOCKET来提供对网络操作的API,Java的提供的NIO SOCKET是一个高效的异步通信API,当然可以在这个基础上来开发我们的网络应用,但这种Native API需要我们花很多精力来处理网络通信的细节,消弱了我们对业务的关心。为我们开发带来很多不便性,幸好Java有很多现成的NIO SOCKET框架可供使用,像Mina,Netty2,xSocket等等,这些框架处理了很多底层的通信问题,提供了一些易用的API以供使用。在这个实例中我们使用Netty2来做通信框架。
定义消息包,消息包有定长包和不定长包,不定长包无非就是要在消息包中加入长度信息,以对收到的网络字节流进行分界。消息包的定义如下 :
4.1.1 确定Flex客户端系统和Java服务器端通信框架(1)
开发异构系统时,如何进行通信和传递数据是我们比较关注的问题。使用Flex+Java开发基于B/S结构企业应用,客户端和服务器端的通信协议是我们所熟知的HTTP协议。在Flex中,基于HTTP协议访问服务器的通信组件有三个:
HttpService(mx.rpc.http.mxml.HTTPService)
WebService(mx.rpc.soap.mxml.WebService)
RemoteObject(mx.rpc.remoting.mxml.RemoteObject)
HttpService组件可以调用很多不同技术的服务端页面,比如JSP、ASP、PHP、Java Servlet、ColdFusion Page等。在大多数情况下,使用HttpService访问服务器端页面来完成Flex客户端与服务器端的数据交互,服务器端返回的结果一般都是XML 格式的数据。下面是Adobe官方关于HTTPService组件的例子应用:
1. <?xml version="1.0" encoding="utf-8"?>
2. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
3. creationComplete="feedRequest.send()" layout="absolute">
4. <mx:HTTPService id="feedRequest"
5. url="http://weblogs.macromedia.com/mchotin/index.xml"
6. useProxy="false" />
7. <mx:Panel x="10" y="10" width="475" height="400"
8. title="{feedRequest.lastResult.rss.channel.title}">
9. <mx:DataGrid id="dgPosts" x="20" y="20" width="400"
10. dataProvider="{feedRequest.lastResult.rss.channel.item}">
11. <mx:columns>
12. <mx:DataGridColumn headerText="Posts" dataField="title"/>
13. <mx:DataGridColumn headerText="Date"
14. dataField="pubDate" width="150" />
15. </mx:columns>
16. </mx:DataGrid>
17. <mx:LinkButton x="20" y="225" label="Read Full Post"
18. click="navigateToURL(new
19. URLRequest(dgPosts.selectedItem.link));"/>
20. <mx:TextArea x="20" y="175" width="400"/>
21. </mx:Panel>
22. </mx:Application>
这个例子的运行需要能够访问互联网,在本例中通过调用URL为http://weblogs . macromedia.com/mchotin/index.xml的HttpService,返回了一个XML文件,并将这个XML作为 DataGrid控件的dataProvider,从而通过DataGrid将XML文件中的数据展示出来。XML中的数据主要是网站最近的发帖记录。这个例子说明,HttpService的工作方式主要通过请求URL获取XML格式数据。
同HttpService类似,Flex 应用可以调用URL所表示WSDL 1.1服务,返回SOAP 1.1格式的调用结果。SOAP也是基于XML格式规范,因此,使用Httpservice和WebService组件同服务器之间的交互都是通过XML 进行的。下面是Adobe官方关于HttpService组件的例子应用。
1. <?xml version="1.0" encoding="utf-8"?>
2. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
3. layout="absolute"
4. creationComplete="wsBlogAggr.getMostPopularPosts.send()">
5. <mx:WebService id="wsBlogAggr"
6. wsdl="http://weblogs.macromedia.com/mxna/webservices/mxna2.cfc?wsdl"
7. useProxy="false">
8. <mx:operation name="getMostPopularPosts">
9. <mx:request>
10. <daysBack>30</daysBack>
11. <limit>{cbxNumPosts.value}</limit>
12. </mx:request>
13. </mx:operation>
14. </mx:WebService>
15. <mx:Panel x="10" y="10" width="475" height="400" layout="absolute"
16. title="Most Popular Posts">
17. <mx:ComboBox x="30" y="25" id="cbxNumPosts"
18. change="wsBlogAggr.getMostPopularPosts.send()">
19. <mx:Object label="Top 5" data="5" />
20. <mx:Object label="Top 10" data="10" />
21. <mx:Object label="Top 15" data="15" />
22. </mx:ComboBox>
23. <mx:DataGrid x="30" y="75" id="dgTopPosts" width="400"
24. dataProvider="{wsBlogAggr.getMostPopularPosts.lastResult}">
25. <mx:columns>
26. <mx:DataGridColumn headerText="Top Posts"
27. dataField="postTitle"/>
28. <mx:DataGridColumn headerText="Clicks" dataField="clicks"
29. width="75"/>
30. </mx:columns>
31. </mx:DataGrid>
32. <mx:LinkButton x="30" y="250"
33. label="Select an item and click here for full post"/>
34. </mx:Panel>
35. </mx:Application>
在这个例子中,通过WebService组件调用了服务器所提供的WebService服务,返回SOAP格式的XML数据,根据请求参数,XML数据表示网站中最近30天的点击率排名前5、10或者15的博客。
通过这两个例子我们可以看到,使用HttpService和WebService无需第三方框架,在服务器端直接编写相应的服务即可,所以比较容易理解和使用。但是,无论使用HttpService还是WebService访问服务器,Flex客户端和服务器之间传递的都是XML数据,客户端和服务器端处理的也是XML数据。对于企业应用来说,客户端和服务器端交互的数据量往往很大,因此使用XML作为数据交换格式会降低传输效率和转换效率。同时,处理XML数据的代码也远比处理对象的代码繁琐,并且难以阅读和调试。因此,在企业应用开发中,客户端系统和服务器端系统之间采用HttpService 和WebService进行通信的部分较少,即使使用这两个组件,也应当用来传递少量、数据格式不易发生变化的数据。
在我们的企业应用开发中,Flex客户端与后台服务器之间的大量通信都是采用RemoteObject完成的。RemoteObject组件在“第三方软件”的配合下,能够调用后台服务器对象上的方法,比如Java对象或者.net对象上的方法,从而实现客户端与服务器端的通信。在客户端使用 RemoteObject可以直接将ActionScript对象作为调用的参数和返回结果。这一点听起来似乎有些神奇,但其实也很容易理解:Adobe 公司定义了一种二进制数据格式AMF(Action Message Format),用于客户端与服务器端的数据交互,AMF数据格式的规范已经公开,有兴趣的读者可以到以下网址下载AMF规范:http://download.macromedia.com/pub/labs/amf/amf3_spec_121207.pdf。
其实,使用AMF格式交换数据与使用XML进行数据交换的主要区别在于:AMF二进制数据的转换和传输效率更高,同时需要“第三方软件”用于解释 AMF格式数据。Flex客户端RemoteObject组件与服务器端通过HTTP协议传递AMF格式的二进制数据进行通信的大致过程如下:
1)客户端RemoteObject将调用参数中的ActionScript对象序列化为AMF数据格式,然后发出调用请求。
2)服务器的“第三方软件”获取HTTP请求流。
3)服务器的“第三方软件”对HTTP请求流进行解析,并且建立响应消息。对HTTP请求流进行解析,解析过程包括解释AMF格式数据,将 ActionScript对象的AMF数据按照事先确定的协议“反序列化”为服务器端对象,比如Java对象,然后用这些参数调用客户端指定的服务器对象上的方法。
4.1.1 确定Flex客户端系统和Java服务器端通信框架(2)
4)服务器的“第三方软件”将调用的结果“序列化”为AMF格式的数据流。
5)服务器发送HTTP响应给Flex客户端。
6)Flex客户端解释AMF格式数据流,将调用结果序列化为ActionScript对象。
7)下面我们给出一段Flex官方文档代码来展示RemoteObject对象的使用:
1. <?xml version="1.0"?>
2. <!-- fds/rpc/ROInAS.mxml -->
3. <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
4. <mx:Script>
5. <![CDATA[
6. import mx.controls.Alert;
7. import mx.rpc.remoting.RemoteObject;
8. import mx.rpc.events.ResultEvent;
9. import mx.rpc.events.FaultEvent;
10. [Bindable]
11. public var empList:Object;
12. public var employeeRO:RemoteObject;
13. public function useRemoteObject(intArg:int, strArg:String):void {
14. employeeRO = new RemoteObject();
15. employeeRO.destination = "SalaryManager";
16. employeeRO.getList.addEventListener("result",
17. getListResultHandler);
18. employeeRO.addEventListener("fault", faultHandler);
19. employeeRO.getList(deptComboBox.selectedItem.data);
20. }
21. public function getListResultHandler(event:ResultEvent):void {
22. // 远程调用成功所要完成的处理。
23. empList=event.result;
24. }
25.
26. public function faultHandler (event:FaultEvent):void {
27. // 调用失败所要完成的处理。
28. Alert.show(event.fault.faultString, 'Error');
29. }
30. ]]>
31. </mx:Script>
32. <mx:ComboBox id="deptComboBox"/>
33. </mx:Application>
在上面的代码中,我们重点关注加黑的代码,首先看一下函数useRemoteObject,这个函数中首先使用语句:
1. employeeRO = new RemoteObject();
创建了一个RemoteObject对象employeeRO。然后通过语句:
1. employeeRO.destination = "SalaryManager";
将RemoteObject对象的destination属性赋值为一个字符串"SalaryManager",destination属性表示远程对象调用的“目的地”,请求发送到服务器端后,服务器端的“第三方软件”接收到请求后会检查配置文件,找到destination值所映射的“服务器端组件”,从而可以调用该组件上的方法。接下来使用语句:
1. employeeRO.getList.addEventListener("result", getListResultHandler);
设置远程服务调用成功时的处理方法,使用语句:
1. employeeRO.addEventListener("fault", faultHandler);
设置远程服务调用失败时的处理方法。最后,使用语句:
1. employeeRO.getList(deptComboBox.selectedItem.data);
以deptComboBox.selectedItem.data为参数,调用destination 属性所映射的“服务器端组件”的getList方法。这里,“服务端组件”必须有一个名为getList的公开方法,调用“服务器端组件”的 getList方法是异步调用,因此它不会阻塞线程来等待调用结果的返回,调用结果的返回时会在getListResultHandler方法中进行处理。在getListResultHandler方法中,我们看到语句:
1. empList=event.result;
该语句表示远程调用所返回的结果(event.result)可以直接赋值给ActionScrip对象。当然,后端返回的对象类型与Flex客户端的ActionScript对象类型要满足“第三方软件”所规定的对象类型之间的“映射”规则,这样,Flex就可以把后台返回的AMF数据流自动地序列化为ActionScript对象。
从上面的代码分析中我们可以看出:同传递XML方式相比,RemoteObject调用方式直接将ActionScript对象作为调用的参数和返回结果,这对于开发者编程特别方便。同时,读者也可能注意到,通过RemoteObject调用远程方法需要多写几行代码,但通过精巧的封装可以很好地解决这个问题。在第7章中,本书作者开发了一个RemoteObject封装框架,使运用RemoteObject对象进行远程方法调用更加优雅简洁,希望读者关注。
在上面的论述中多次提到了“第三方软件”,要使用RemoteObject组件进行远程方法调用,那么必须在服务器上部署和配置相应的“第三方软件”。“第三方软件”有两个最基本的作用:
服务器端对象序列化为AMF格式数据和将AMF格式数据反序列化为服务器端对象。
将客户端的请求映射为服务器端相应对象上的方法调用。
由于AMF规范已经公开,因此,有很多“第三方软件”支持不同的后台服务器端语言,在.net平台下比较著名的“第三方软件”为Midnight Coders WebORB(http://www.themidnightcoders.com/)。我们所关心的Java平台下的“第三方软件”有Adobe官方商业收费软件LifeCycle Data Services(LCDS)和Adobe官方开源软件BlazeDS。
BlazeDS是LCDS的开源版,只不过BlazeDS不具备LCDS的一些高级功能,比如:
高级客户端-服务器数据同步功能。
冲突检测/解决。
Adobe AIR应用的离线数据管理服务。
由RIA生成PDF等。
使用BlazeDS与LCDS进行企业应用开发的配置完全一样,因此,在不需要LCDS高级功能的情况下,完全可以使用BlazeDS替换LCDS 作为一种廉价方案,必须使用LCDS高级功能时,用户可以追加投资购买LCDS,因此基于BlazeDS企业应用可以很容易升级为基于LCDS的企业应用,这也是我们选择BlazeDS作为配合RemoteObject远程调用的“第三方软件”的主要原因。
因此,使用Flex+Java开发企业应用,我们主要使用RemoteObject+BlazeDS实现Flex端与Java端的通信。读者可以从以下网址查看BlazeDS的信息和下载BlazeDS框架:http://opensource.adobe.com/wiki/display/blazeds/BlazeDS/。
总之,使用RemoteObject+BlazeDS作为Flex端同Java服务器端的通信框架有如下优点:
以二进制的AMF协议传递数据,转换和传输数据的性能高于XML格式,读者可以参考http://www.javaeye.com/news/3000一文中对几种RIA应用数据传输协议性能的比较。
使用RemoteObject+BlazeDS能够实现Flex对象与Java对象之间的自动转换,更加有利于开发者编程。
使用开源框架BlazeDS所开发的企业应用可以更容易地升级为采用高端商业软件LCDS作为数据通信框架的企业应用。
在第5章中,我们对BlazeDS框架进行了较为详细的介绍;在第7章中,我们在BlazeDS基础上进行了一些二次开发,使得BlazeDS框架更加适合多模块、大规模企业应用的部署和开发。当然,还有一些其他开源框架,比如Hessian和Granite也能够完成与BlazeDS类似的功能,开发者可以根据实际情况加以选择,但是由于它们的原理相同,所以可以使用相同架构方法和设计模式。