zmqSocket 使用和相关java后台准备
zmqSocket.as 源码地址: http://zmqsocket-as.googlecode.com/svn/
zmqSocket.js 源码地址: http://zmqsocket-js.googlecode.com/svn/
zmqSocket.as是通过flex自带的socket在前端实现和后台消息通讯的一个简单类库,本来想简单配置下zmqSocket的使用,结果发现各种限制各种问题,整理出最后的使用规范和注意事项如下:
首先关于后台(小弟使用java编写的后台,其他语言这里仅供参考):
1、首先是跨域文件的获取,flex默认回去端口843寻找垮与文件,发送请求"<policy-file-request/>",并希望获得类似下列格式的跨域许可文件。
<?xml version="1.0"?> <cross-domain-policy> <site-control permitted-cross-domain-policies="all"/> <allow-access-from domain="IP地址或*" to-ports="socket打开的端口" /> </cross-domain-policy>
所以后台服务最好提供这样一个端口来专门负责处理
另外的一个解决方式是在客户端直接使用Security.loadPolicyFile(url); ,从指定的url去获取垮与文件。
2、关于服务端设置,因为socket是以二进制字节流传输信息的,所以要求服务端也应该是针字节流进行解析(xmlSocket貌似没有这样的问题,后面测测再补充下,如果大家有其他方案也可以告知我下,互相学习)。
*****服务端采用的方式,通过读取二进制数组,并将二进制数组接写成字符转来做最终的转义,写入的时候也是直接写入二进制字符串
附上一个java端实现许可文件提供的服务以供参考:
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; public class FlexServer2 { public static void main(String args[]) { try { DataInputStream din; DataOutputStream dout; ServerSocket flexServer = new ServerSocket(843); while (true) { Socket client = flexServer.accept(); din = new DataInputStream(client.getInputStream()); dout = new DataOutputStream(client.getOutputStream()); String s; String backFile = "<?xml version=\"1.0\"?>" + "<cross-domain-policy>" + "<site-control permitted-cross-domain-policies=\"all\"/>" + "<allow-access-from domain=\"*\" to-ports=\"*\" />" + "</cross-domain-policy>"; // 设置一个长度空间的二进制数组来获取客户端信息 byte[] backBytes = backFile.getBytes("utf-8"); while (true) { byte[] b = new byte[2048]; int length = din.read(b, 0, b.length); if (length != -1) { s = new String(b, 0, length); System.out.println("843读到的信息:" + s); if (s.equals("<policy-file-request/>")) { dout.write(backBytes); dout.flush(); } else { break; } } } din.close(); dout.close(); client.close(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
服务端需要注意的也就这两点了,在客户端直接使用zmqSocket也有些严格要求的地方,细列如下:
客户端注意实现:
1、信息接收,提供类似事件回调的方式。 这点不同于习以为常的java或者c++代码,通过一个线程等待去轮询获取接收信息。(因为是flash是单线程的,如果等待意味着其他事都做不了了)。
比如直接使用socket,接收信息的办法是在 ProgressEvent.SOCKET_DATA 事件中去 判断bytesAvailable 并调用类似socket.readUTFBytes(bytesAvailable)的方法去获取信息。
关于直接使用socket的Demo请直接查看 Flex 4.0的API文档中的示例,很简单。
2、补充1. 关于zmqSocket中,内部添加一个关于信息的接收栈,只有当信息接收到足够多内容才会触发 Message 事件,(保留了跟socket默认的事件回调风格)也只有在这个时候嗲用recv()方法才能获取到信息。否则在下一次消息来临之后则会覆盖之前的信息。
关于这点要说明的是,当服务端信息过短的时候,是可能不会触发Message事件的,即意味着客户端可能没法立刻通过事件回调读取信息(关于这块应该是有可设置选项的,待研究吧)
给出一个直接使用zmqSocket.as的示范代码,来源于zmqSocket的官方测试代码:
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" creationComplete="initSocket()" > <fx:Script> <![CDATA[ private function initSocket():void { var input: TextField = new TextField; input.type = TextFieldType.INPUT; input.background = true; input.border = true; input.width = 350; input.height = 350; ucom.addChild(input); input.appendText ("Starting\n"); try { var socket: ZmqSocket = new ZmqSocket (); } catch (e: Error) { input.appendText ("Error: " + e.toString ()); return; } socket.addEventListener (ZmqSocket.OPEN, function (e: Event) { input.appendText ("opened\n"); socket.send (["", "Test"]); }); socket.addEventListener (ZmqSocket.MESSAGE, function (e: Event) { var msg: Array = socket.recv (); input.appendText (msg.length + "\n"); if (msg.length > 1) input.appendText (msg[1] + "\n"); socket.send (["", "Test"]); }); socket.addEventListener (Event.CLOSE, function (e: Event) { input.appendText ("closed\n"); }); socket.addEventListener (IOErrorEvent.IO_ERROR, function (e: IOErrorEvent) { input.appendText (e.toString ()+"\n"); }); socket.addEventListener (SecurityErrorEvent.SECURITY_ERROR, function (e: SecurityErrorEvent) { input.appendText (e.toString ()+"\n"); }); socket.connect ("localhost", 2000); } ]]> </fx:Script> <s:layout> <s:BasicLayout/> </s:layout> <mx:UIComponent id="ucom"/> <fx:Declarations> <!-- 将非可视元素(例如服务、值对象)放在此处 --> </fx:Declarations> </s:Application>
3、追加3, 问题仍然是关于zmqSocket.as源码的。小弟下到的版本在信息发送时没有socket.flush() 这行代码的调用,导致服务端一直无法获得信息。补充后测试OK
注:这个在源码svn的的提交记录中是有过修改的,不知道为什么小弟这个版本没有,提醒大家遇到同样的问题可以自己修改下,重新编译swf,免得纠结...
总结,目前发现的问题就是上述了, 都是很小的细节,但很让人恼火..... 希望对大家有所帮助:
最后,简单实用as貌似不会太多,我们看看直接使用zmqSocket.js的,这里是通过客户端js与as的交互,来实现第一个普通网页的请求处理。
这里直接使用官方获取到的示例代码。zmqSocket和zmqSocketMain源码重新编译,生成新的zmqSocketMain.swf文件。配合上述的服务端代码测试,OK!!!O(∩_∩)O~
注意:发现的小问题,在本地直接双击打开网页的时候可能会有ExternalInterface.calll方法跨域错误。 解决方案1:修改flashPlayerTrust文件下的配置。2:发布网页通过ip地址访问测试
示范代码如下:
//////////file zmqSocket.js (function() { // Check if module is already initialized. if (window.ZmqSocket) return; // Constructor. You may pass socket identity here. ZmqSocket = function(identity) { this.fd = ZmqSocket.__nextFd++; ZmqSocket.__sockets[this.fd] = this; this.identity = identity; this.state = ZmqSocket.CONNECTING; this.onopen = function() { }; this.onmessage = function() { }; this.onerror = function(msg) { }; this.onclose = function() { }; var athis = this; ZmqSocket.__addTask(function() { ZmqSocket.__flash.create(athis.fd, athis.identity); }); }; ZmqSocket.__sockets = []; ZmqSocket.__tasks = []; ZmqSocket.__nextFd = 0; ZmqSocket.__flash = null; ZmqSocket.CONNECTING = 1; ZmqSocket.OPEN = 2; ZmqSocket.CLOSING = 3; // Called by Flash when it's ready. ZmqSocket.__onFlashReady = function() { if (navigator.appName.indexOf("Microsoft") != -1) ZmqSocket.__flash = window["ZmqSocketFlash"]; else ZmqSocket.__flash = document["ZmqSocketFlash"]; // Make it later to avoid recursion. ZmqSocket.__later(function() { for (var i = 0; i < ZmqSocket.__tasks.length; i++) ZmqSocket.__tasks[i](); }); }; // Used to make calls in a separate event handler to avoid // recursive calls to Flash - recursion is not supported by many browsers. ZmqSocket.__later = function(task) { var to = setTimeout(function() { clearTimeout(to); task(); }, 0); } // Called by Flash to find out if JS is ready. ZmqSocket.__isJSReady = function() { return true; }; // Called when we are not sure that flash is ready. ZmqSocket.__addTask = function(task) { if (ZmqSocket.__flash) task(); else ZmqSocket.__tasks.push(task); } ZmqSocket.prototype.connect = function(host, port) { var athis = this; ZmqSocket.__addTask(function() { ZmqSocket.__flash.connect(athis.fd, host, port); }); } ZmqSocket.prototype.close = function() { this.state = ZmqSocket.CLOSING; var athis = this; ZmqSocket.__addTask(function() { alert('before'); ZmqSocket.__flash.close(athis.fd); alert('after'); }); } ZmqSocket.prototype.available = function() { if (this.state != ZmqSocket.OPEN) throw "Invalid state, socket must be connected!"; return ZmqSocket.__flash.available(this.fd); } ZmqSocket.prototype.recv = function() { if (this.state != ZmqSocket.OPEN) throw "Invalid state, socket must be connected!"; return ZmqSocket.__flash.recv(this.fd); } ZmqSocket.prototype.send = function(msg) { if (this.state != ZmqSocket.OPEN) throw "Invalid state, socket must be connected!"; return ZmqSocket.__flash.send(this.fd, msg); } ZmqSocket.__onopen = function(fd) { // Avoiding recursion ZmqSocket.__later(function() { ZmqSocket.__sockets[fd].state = ZmqSocket.OPEN; ZmqSocket.__sockets[fd].onopen(); }); } ZmqSocket.__onmessage = function(fd) { // Avoiding recursion ZmqSocket.__later(function() { ZmqSocket.__sockets[fd].onmessage(); }); } ZmqSocket.__onerror = function(fd, msg) { // Avoiding recursion ZmqSocket.__later(function() { ZmqSocket.__sockets[fd].onerror(msg); }); } ZmqSocket.__onclose = function(fd) { // Avoiding recursion ZmqSocket.__later(function() { ZmqSocket.__sockets[fd].onclose(); }); } })();
/////////file Test.html <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>ZmqSocket.js</title> <script language="JavaScript" src='ZmqSocket.js'> </script> </head> <body> <script language="JavaScript"> var socket = new ZmqSocket(); var ready = false; socket.onopen = function() { ready = true; }; socket.onmessage = function() { if (socket.available()) { var m = socket.recv(); alert(m); } else { alert('Assertion failure!'); } }; socket.onclose = function() { alert('close'); }; socket.onerror = function(msg) { alert(msg); }; socket.connect("localhost", 2000); </script> <input type='text' id='in' /> <input type='submit' value='Send' onclick='if (!ready) return; var t = document.getElementById("in").value; socket.send (["", t]); ' /> <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" id="ZmqSocketFlash" width="0" height="0" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab"> <param name="movie" value="ZmqSocketMain.swf" /> <param name="allowScriptAccess" value="sameDomain" /> <embed src="ZmqSocketMain.swf" quality="high" width="0" height="0" name="ZmqSocketFlash" align="middle" play="true" loop="false" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> </embed> </object> </body> </html>