让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的原理。事实上,直接用flash的socket就可以完成与服务端的双向信息传递,不过缺点是需要在服务端的socket代码写很多其他的代码,比如多线程,信息解析等,直接用websocket就不用写那些多余的代码了。
我花了点时间把源码里面和加密有关的代码都注释掉,加密的代码都是和wss(类似于https)有关的,用flash编译了一下,主要就是把mx包加到flash编译里面,flex是臃肿版的flash,结果大小只有20k!原来加密让文件大太多了!
websocket.js
define("websocket", function() {
(function() {
if (window.WEB_SOCKET_FORCE_FLASH) {
// Keeps going.
}
else if (window.WebSocket) {
return;
} else if (window.MozWebSocket) {
// Firefox.
window.WebSocket = MozWebSocket;
return;
}
var logger;
if (window.WEB_SOCKET_LOGGER) {
logger = WEB_SOCKET_LOGGER;
} else if (window.console && window.console.log && window.console.error) {
logger = window.console;
} else {
logger = {log: function(){ }, error: function(){ }};
}
window.WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
var self = this;
self.__id = WebSocket.__nextId++;
WebSocket.__instances[self.__id] = self;
self.readyState = WebSocket.CONNECTING;
self.bufferedAmount = 0;
self.__events = {};
if (!protocols) {
protocols = [];
} else if (typeof protocols == "string") {
protocols = [protocols];
}
self.__createTask = setTimeout(function() {
WebSocket.__addTask(function() {
self.__createTask = null;
WebSocket.__flash.create(
self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
});
}, 0);
};
WebSocket.prototype.send = function(data) {
if (this.readyState == WebSocket.CONNECTING) {
throw "INVALID_STATE_ERR: Web Socket connection has not been established";
}
var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
if (result < 0) { // success
return true;
} else {
this.bufferedAmount += result;
return false;
}
};
WebSocket.prototype.close = function() {
if (this.__createTask) {
clearTimeout(this.__createTask);
this.__createTask = null;
this.readyState = WebSocket.CLOSED;
return;
}
if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
return;
}
this.readyState = WebSocket.CLOSING;
WebSocket.__flash.close(this.__id);
};
WebSocket.prototype.dispatchEvent = function(event) {
var events = this.__events[event.type] || [];
for (var i = 0; i < events.length; ++i) {
events[i](event);
}
var handler = this["on" + event.type];
if (handler) handler.apply(this, [event]);
};
WebSocket.prototype.__handleEvent = function(flashEvent) {
if ("readyState" in flashEvent) {
this.readyState = flashEvent.readyState;
}
if ("protocol" in flashEvent) {
this.protocol = flashEvent.protocol;
}
var jsEvent;
if (flashEvent.type == "open" || flashEvent.type == "error") {
jsEvent = this.__createSimpleEvent(flashEvent.type);
} else if (flashEvent.type == "close") {
jsEvent = this.__createSimpleEvent("close");
jsEvent.wasClean = flashEvent.wasClean ? true : false;
jsEvent.code = flashEvent.code;
jsEvent.reason = flashEvent.reason;
} else if (flashEvent.type == "message") {
var data = decodeURIComponent(flashEvent.message);
jsEvent = this.__createMessageEvent("message", data);
} else {
throw "unknown event type: " + flashEvent.type;
}
this.dispatchEvent(jsEvent);
};
WebSocket.prototype.__createSimpleEvent = function(type) {
if (document.createEvent && window.Event) {
var event = document.createEvent("Event");
event.initEvent(type, false, false);
return event;
} else {
return {type: type, bubbles: false, cancelable: false};
}
};
WebSocket.prototype.__createMessageEvent = function(type, data) {
if (window.MessageEvent && typeof(MessageEvent) == "function" && !window.opera) {
return new MessageEvent("message", {
"view": window,
"bubbles": false,
"cancelable": false,
"data": data
});
} else if (document.createEvent && window.MessageEvent && !window.opera) {
var event = document.createEvent("MessageEvent");
event.initMessageEvent("message", false, false, data, null, null, window, null);
return event;
} else {
return {type: type, data: data, bubbles: false, cancelable: false};
}
};
WebSocket.CONNECTING = 0;
WebSocket.OPEN = 1;
WebSocket.CLOSING = 2;
WebSocket.CLOSED = 3;
WebSocket.__isFlashImplementation = true;
WebSocket.__initialized = false;
WebSocket.__flash = null;
WebSocket.__instances = {};
WebSocket.__tasks = [];
WebSocket.__nextId = 0;
WebSocket.loadFlashPolicyFile = function(url){
WebSocket.__addTask(function() {
WebSocket.__flash.loadManualPolicyFile(url);
});
};
WebSocket.__initialize = function() {
if (WebSocket.__initialized) return;
WebSocket.__initialized = true;
if (WebSocket.__swfLocation) {
window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
}
};
WebSocket.__onFlashInitialized = function() {
setTimeout(function() {
WebSocket.__flash =main.get_flash_obj("webSocketFlash");
WebSocket.__flash.setCallerUrl(location.href);
WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
for (var i = 0; i < WebSocket.__tasks.length; ++i) {
WebSocket.__tasks[i]();
}
WebSocket.__tasks = [];
}, 0);
};
WebSocket.__onFlashEvent = function() {
setTimeout(function() {
try {
var events = WebSocket.__flash.receiveEvents();
for (var i = 0; i < events.length; ++i) {
WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
}
} catch (e) {
logger.error(e);
}
}, 0);
return true;
};
WebSocket.__log = function(message) {
logger.log(decodeURIComponent(message));
};
WebSocket.__error = function(message) {
logger.error(decodeURIComponent(message));
};
WebSocket.__addTask = function(task) {
if (WebSocket.__flash) {
task();
} else {
WebSocket.__tasks.push(task);
}
};
})();
return WebSocket;
});
websocket_main.js
require(['html5/websocket','avalon-min'],function(WebSocket,avalon){
var $=function(id){
return document.getElementById(id);
};
WEB_SOCKET_DEBUG = true;
var ws;
ws = new WebSocket("ws://localhost:8888/new-msg/socket");
ws.onopen = function() {
output("onopen");
};
ws.onmessage = function(e) {
output("onmessage: " + e.data);
};
ws.onclose = function() {
output("onclose");
};
ws.onerror = function() {
output("onerror");
};
avalon.bind($('send'),'click',function(){
var input = $("input1");
ws.send(input.value);
output("send: " + input.value);
input.value = "";
input.focus();
});
avalon.bind($('close'),'click',function(){
ws.close();
});
function output(str) {
var log = document.getElementById("log");
var escaped = str.replace(/&/, "&").replace(/</, "<")
.replace(/>/, ">").replace(/"/, """); // "
log.innerHTML = escaped + "<br>" + log.innerHTML;
}
});
HTML:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script src="http://localhost/twitter/js/libs/seed-min.js"></script>
<script type="text/javascript" src="http://localhost/twitter/js/libs/flash_embed.js"></script>
<script src="http://localhost/twitter/js/main.js" type="text/javascript"></script>
</head>
<body>
<input type="text" id="input1">
<input type="submit" value="Send" id='send'>
<button id='close'>close</button>
<div id="log"></div>
<div id='a' style='width: 1px; height: 1px;'></div>
<script type="text/javascript">
flash_object.embedSWF('http://localhost:8888/swf/WebSocketMain.swf', 'a', 'webSocketFlash', '100%', '100%');
</script>
<script type="text/javascript" src='http://localhost/twitter/js/libs/html5/websocket_main.js'></script>
</body>
</html>
下面很重要,在运行前一定要开启服务端(python)的socket
import socket
import time
from threading import Thread
class returnCrossDomain(Thread):
def __init__(self,connection):
Thread.__init__(self)
self.con = connection
def run(self):
clientData = self.con.recv(1024)
xmlData = '''<?xml version="1.0" encoding="utf-8"?>'''
xmlData += '''<cross-domain-policy><policy-file-request/>'''
xmlData += '''<allow-access-from domain="*" to-ports="*" />'''
xmlData += '''</cross-domain-policy>\0'''
try:
self.con.send(xmlData)
except Excepiton,e:
pass
self.con.close()
def main():
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.bind(('localhost',843))
sock.listen(10000000)
print 'socket'
while True:
try:
connection,address = sock.accept()
returnCrossDomain(connection).start()
except:
time.sleep(1)
if __name__=="__main__":
main()
服务端用python的tornado写的,也一样在运行前开启tornado
# -*- coding: utf-8 -*-
import base
import tornado.websocket
def send_message(message):
for handler in ChatSocketHandler.socket_handlers:
handler.write_message(message)
class ChatSocketHandler(tornado.websocket.WebSocketHandler):
socket_handlers = set()
def open(self):
ChatSocketHandler.socket_handlers.add(self)
print 'websocket'
send_message('A new user has entered the chat room.')
def on_close(self):
ChatSocketHandler.socket_handlers.remove(self)
print 'websocket close'
send_message('A user has left the chat room.')
def on_message(self, message):
print 'message:'+message
send_message(message)
tornado很好的封装了websocket,用起来很简单,加上flash模拟websocket兼容不支持websocket的浏览器,这样可以完美的利用高效的websocket.
如果嫌弃flash的话,还是用ajax长连接,tornado很好的支持ajax长连接,性能很好.
socket.io也是个不错的选择,提供了多种传输方案。
WebSocketMain.swf文件及源码见附件
-
本文附件下载:
- flash_websocket.zip (320.1 KB)
已有 0 人发表留言,猛击->> 这里<<-参与讨论
ITeye推荐