让ie6 7 8 9支持原生html5 websocket
从github上的 web-socket-js (socket.io好像也是用这个做的他们的flash替代传输方式)改过来的。不过值得注意的是里面的flash websocket代理文件,文件实在是很大,有174k 很好奇,就反编译看下, 是flex做的,这点很不喜欢,因为我没有flex builder也不想因为去改代码重新装一个,然后mx包下面的是flex的组件,com包下是adobe封装的socket和两个加密包 . 最下面那个包才是最主要的,代码不是很复杂,就是利用actionscript3的socket,与那边服务端socket握手,传递消息,不过区别是,在connect的时候要把header封装成websocket的样子
这也是flash模拟websocket的原理。 我花了点时间把源码里面和加密有关的代码都注释掉,加密的代码都是和wss(类似于https)有关的,用flash编译了一下,主要就是把mx包加到flash编译里面,flex是臃肿版的flash,结果大小只有20k!原来加密让文件大太多了! websocket.js 1 define("websocket", function() { 2 (function() { 3 if (window.WEB_SOCKET_FORCE_FLASH) { 4 // Keeps going. 5 } 6 else if (window.WebSocket) { 7 return; 8 } else if (window.MozWebSocket) { 9 // Firefox. 10 window.WebSocket = MozWebSocket; 11 return; 12 } 13 var logger; 14 if (window.WEB_SOCKET_LOGGER) { 15 logger = WEB_SOCKET_LOGGER; 16 } else if (window.console && window.console.log && window.console.error) { 17 logger = window.console; 18 } else { 19 logger = {log: function(){ }, error: function(){ }}; 20 } 21 window.WebSocket = function(url, protocols, proxyHost, proxyPort, headers) { 22 var self = this; 23 self.__id = WebSocket.__nextId++; 24 WebSocket.__instances[self.__id] = self; 25 self.readyState = WebSocket.CONNECTING; 26 self.bufferedAmount = 0; 27 self.__events = {}; 28 if (!protocols) { 29 protocols = []; 30 } else if (typeof protocols == "string") { 31 protocols = [protocols]; 32 } 33 self.__createTask = setTimeout(function() { 34 WebSocket.__addTask(function() { 35 self.__createTask = null; 36 WebSocket.__flash.create( 37 self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null); 38 }); 39 }, 0); 40 }; 41 WebSocket.prototype.send = function(data) { 42 if (this.readyState == WebSocket.CONNECTING) { 43 throw "INVALID_STATE_ERR: Web Socket connection has not been established"; 44 } 45 var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data)); 46 if (result < 0) { // success 47 return true; 48 } else { 49 this.bufferedAmount += result; 50 return false; 51 } 52 }; 53 WebSocket.prototype.close = function() { 54 if (this.__createTask) { 55 clearTimeout(this.__createTask); 56 this.__createTask = null; 57 this.readyState = WebSocket.CLOSED; 58 return; 59 } 60 if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) { 61 return; 62 } 63 this.readyState = WebSocket.CLOSING; 64 WebSocket.__flash.close(this.__id); 65 }; 66 WebSocket.prototype.dispatchEvent = function(event) { 67 var events = this.__events[event.type] || []; 68 for (var i = 0; i < events.length; ++i) { 69 events[i](event); 70 } 71 var handler = this["on" + event.type]; 72 if (handler) handler.apply(this, [event]); 73 }; 74 WebSocket.prototype.__handleEvent = function(flashEvent) { 75 if ("readyState" in flashEvent) { 76 this.readyState = flashEvent.readyState; 77 } 78 if ("protocol" in flashEvent) { 79 this.protocol = flashEvent.protocol; 80 } 81 var jsEvent; 82 if (flashEvent.type == "open" || flashEvent.type == "error") { 83 jsEvent = this.__createSimpleEvent(flashEvent.type); 84 } else if (flashEvent.type == "close") { 85 jsEvent = this.__createSimpleEvent("close"); 86 jsEvent.wasClean = flashEvent.wasClean ? true : false; 87 jsEvent.code = flashEvent.code; 88 jsEvent.reason = flashEvent.reason; 89 } else if (flashEvent.type == "message") { 90 var data = decodeURIComponent(flashEvent.message); 91 jsEvent = this.__createMessageEvent("message", data); 92 } else { 93 throw "unknown event type: " + flashEvent.type; 94 } 95 this.dispatchEvent(jsEvent); 96 }; 97 WebSocket.prototype.__createSimpleEvent = function(type) { 98 if (document.createEvent && window.Event) { 99 var event = document.createEvent("Event"); 100 event.initEvent(type, false, false); 101 return event; 102 } else { 103 return {type: type, bubbles: false, cancelable: false}; 104 } 105 }; 106 WebSocket.prototype.__createMessageEvent = function(type, data) { 107 if (window.MessageEvent && typeof(MessageEvent) == "function" && !window.opera) { 108 return new MessageEvent("message", { 109 "view": window, 110 "bubbles": false, 111 "cancelable": false, 112 "data": data 113 }); 114 } else if (document.createEvent && window.MessageEvent && !window.opera) { 115 var event = document.createEvent("MessageEvent"); 116 event.initMessageEvent("message", false, false, data, null, null, window, null); 117 return event; 118 } else { 119 return {type: type, data: data, bubbles: false, cancelable: false}; 120 } 121 }; 122 WebSocket.CONNECTING = 0; 123 WebSocket.OPEN = 1; 124 WebSocket.CLOSING = 2; 125 WebSocket.CLOSED = 3; 126 WebSocket.__isFlashImplementation = true; 127 WebSocket.__initialized = false; 128 WebSocket.__flash = null; 129 WebSocket.__instances = {}; 130 WebSocket.__tasks = []; 131 WebSocket.__nextId = 0; 132 WebSocket.loadFlashPolicyFile = function(url){ 133 WebSocket.__addTask(function() { 134 WebSocket.__flash.loadManualPolicyFile(url); 135 }); 136 }; 137 WebSocket.__initialize = function() { 138 if (WebSocket.__initialized) return; 139 WebSocket.__initialized = true; 140 if (WebSocket.__swfLocation) { 141 window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation; 142 } 143 }; 144 WebSocket.__onFlashInitialized = function() { 145 setTimeout(function() { 146 WebSocket.__flash =main.get_flash_obj("webSocketFlash"); 147 WebSocket.__flash.setCallerUrl(location.href); 148 WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG); 149 for (var i = 0; i < WebSocket.__tasks.length; ++i) { 150 WebSocket.__tasks[i](); 151 } 152 WebSocket.__tasks = []; 153 }, 0); 154 }; 155 WebSocket.__onFlashEvent = function() { 156 setTimeout(function() { 157 try { 158 var events = WebSocket.__flash.receiveEvents(); 159 for (var i = 0; i < events.length; ++i) { 160 WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]); 161 } 162 } catch (e) { 163 logger.error(e); 164 } 165 }, 0); 166 return true; 167 }; 168 WebSocket.__log = function(message) { 169 logger.log(decodeURIComponent(message)); 170 }; 171 WebSocket.__error = function(message) { 172 logger.error(decodeURIComponent(message)); 173 }; 174 WebSocket.__addTask = function(task) { 175 if (WebSocket.__flash) { 176 task(); 177 } else { 178 WebSocket.__tasks.push(task); 179 } 180 }; 181 })(); 182 return WebSocket; 183 }); websocket_main.js 1 require(['html5/websocket','avalon-min'],function(WebSocket,avalon){ 2 var $=function(id){ 3 return document.getElementById(id); 4 }; 5 WEB_SOCKET_DEBUG = true; 6 var ws; 7 ws = new WebSocket("ws://localhost:8888/new-msg/socket"); 8 ws.onopen = function() { 9 output("onopen"); 10 }; 11 ws.onmessage = function(e) { 12 output("onmessage: " + e.data); 13 }; 14 ws.onclose = function() { 15 output("onclose"); 16 }; 17 ws.onerror = function() { 18 output("onerror"); 19 }; 20 avalon.bind($('send'),'click',function(){ 21 var input = $("input1"); 22 ws.send(input.value); 23 output("send: " + input.value); 24 input.value = ""; 25 input.focus(); 26 }); 27 avalon.bind($('close'),'click',function(){ 28 ws.close(); 29 }); 30 function output(str) { 31 var log = document.getElementById("log"); 32 var escaped = str.replace(/&/, "&").replace(/</, "<") 33 .replace(/>/, ">").replace(/"/, """); // " 34 log.innerHTML = escaped + "<br>" + log.innerHTML; 35 } 36 }); 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 5 <title>Insert title here</title> 6 <script src="http://localhost/twitter/js/libs/seed-min.js"></script> 7 <script type="text/javascript" 8 src="http://localhost/twitter/js/libs/flash_embed.js"></script> 9 <script src="http://localhost/twitter/js/main.js" type="text/javascript"></script> 10 </head> 11 <body> 12 <input type="text" id="input1"> 13 <input type="submit" value="Send" id='send'> 14 <button id='close'>close</button> 15 <div id="log"></div> 16 <div id='a' style='width: 1px; height: 1px;'></div> 17 <script type="text/javascript"> 18 flash_object.embedSWF('http://localhost:8888/swf/WebSocketMain.swf', 19 'a', 'webSocketFlash', '100%', '100%'); 20 </script> 21 <script type="text/javascript" 22 src='http://localhost/twitter/js/libs/html5/websocket_main.js'></script> 23 </body> 24 </html> 下面很重要,在运行前一定要开启服务端(python)的socket 1 import socket 2 import time 3 from threading import Thread 4 5 class returnCrossDomain(Thread): 6 def __init__(self,connection): 7 Thread.__init__(self) 8 self.con = connection 9 def run(self): 10 clientData = self.con.recv(1024) 11 xmlData = '''<?xml version="1.0" encoding="utf-8"?>''' 12 xmlData += '''<cross-domain-policy><policy-file-request/>''' 13 xmlData += '''<allow-access-from domain="*" to-ports="*" />''' 14 xmlData += '''</cross-domain-policy>\0''' 15 try: 16 self.con.send(xmlData) 17 except Excepiton,e: 18 pass 19 self.con.close() 20 def main(): 21 sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 22 sock.bind(('localhost',843)) 23 sock.listen(10000000) 24 print 'socket' 25 while True: 26 try: 27 connection,address = sock.accept() 28 returnCrossDomain(connection).start() 29 except: 30 time.sleep(1) 31 32 if __name__=="__main__": 33 main() 服务端用python的tornado写的,也一样在运行前开启tornado 1 # -*- coding: utf-8 -*- 2 import base 3 import tornado.websocket 4 5 def send_message(message): 6 for handler in ChatSocketHandler.socket_handlers: 7 handler.write_message(message) 8 9 class ChatSocketHandler(tornado.websocket.WebSocketHandler): 10 socket_handlers = set() 11 12 def open(self): 13 ChatSocketHandler.socket_handlers.add(self) 14 print 'websocket' 15 send_message('A new user has entered the chat room.') 16 17 def on_close(self): 18 ChatSocketHandler.socket_handlers.remove(self) 19 print 'websocket close' 20 send_message('A user has left the chat room.') 21 22 def on_message(self, message): 23 print 'message:'+message 24 send_message(message) tornado很好的封装了websocket,用起来很简单,加上flash模拟websocket兼容不支持websocket的浏览器,这样可以完美的利用高效的websocket. 如果嫌弃flash的话,还是用ajax长连接,tornado很好的支持ajax长连接,性能很好 WebSocketMain.swf文件及源码:http://files.cnblogs.com/TheViper/flash_websocket.zip |