请稍等...页面即将加载完成
关于websocket的python实现
又是闲来无事,熟悉一下websocket相关东东,写了一个小demo,才发现不能用,最后搜索发现websocket连接有一个握手过程,这个过程和flash的鉴权是类似操作。来看看一个websocket协议的数据包结构吧,如果不喜欢看英文,可以看这篇博客的简介:http://blog.csdn.net/fenglibing/article/details/6852497:

欲了解详情,可以访问:http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10了解情况,在这个版本之前还有一个老版协议:http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-02,貌似safari浏览器还用的该协议,需要了解可以直接去查看,下面贴一个websocket通信协议帮助类:
View Code PYTHON
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | import hashlib import base64 class WebSocket: def __init__(self, protocols = []): self.header = {} self.protocols = protocols def trim(self, str): '''去除空白字符''' rep = ['\n', '\r', ' '] for i in rep: str = str.replace(i, '') return str def unpack_data(self, data): '''解析数据包''' if self.header['Version'] == 1: #老版本 return data[1:-1] code_length = ord(data[1]) & 127 if code_length == 126: masks = data[4:8] data = data[8:] elif code_length == 127: masks = data[10:14] data = data[14:] else: masks = data[2:6] data = data[6:] i = 0 raw_str = '' for d in data: raw_str += chr(ord(d) ^ ord(masks[i%4])) i += 1 return raw_str def pack_data(self, str): '''打包数据''' if self.header['Version'] == 1: #老版本 return '\x00'+str+'\xFF' data = '\x81' dlen = len(str) if dlen <= 125: data += chr(dlen) else: data + chr(126) data + chr(dlen>>8) data + chr(dlen&0xFF) return data + str def get_token1(self, key1, key2, key3): '''老版本token算法''' num1 = int(''.join([i for i in key1 if i.isdigit()])) spa1 = key1.count(' ') num2 = int(''.join([i for i in key2 if i.isdigit()])) spa2 = key2.count(' ') combined = struct.pack('>II', num1/spa1, num2/spa2) + key3 return hashlib.md5(combined).digest() def get_hand_shake1(self): '''老版本头握手消息''' data = 'HTTP/1.1 101 WebSocket Protocol Handshake\r\n' data += 'Upgrade: WebSocket\r\n' data += 'Connection: Upgrade\r\n' data += 'Sec-WebSocket-Origin: %s\r\n' data += 'Sec-WebSocket-Location: %s\r\n' data += 'Sec-WebSocket-Protocol: %s\r\n\r\n' data = data % (self.header['Sec-WebSocket-Origin'], self.header['Sec-WebSocket-Location'], ','.join(self.protocols)) return data def get_token2(self, key): '''新版本token算法''' key += '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' skey = hashlib.sha1(key).digest() return base64.b64encode(skey) def get_hand_shake2(self): '''新版本头握手消息''' data = 'HTTP/1.1 101 Switching Protocols\r\n' data += 'Upgrade: websocket\r\n' data += 'Connection: Upgrade\r\n' data += 'Sec-WebSocket-Accept: %s\r\n' data += 'Sec-WebSocket-Protocol: %s\r\n\r\n' data = data % (self.header['Token'], ','.join(self.protocols)) return data def decode_header(self, msg): '''解析websocket握手消息''' ret = msg.strip('\r\n').split('\r\n') for i in ret: h = i.split(':', 1) self.header[self.trim(h[0])] = self.trim(h[1]) self.header['Sec-WebSocket-Location'] = 'ws://'+self.header['Host'] if self.header.has_key('Sec-WebSocket-Key1'): self.header['Version'] = 1 self.header['Token'] = self.get_token1(self.header['Sec-WebSocket-Key1'], self.header['Sec-WebSocket-Key2'], msg[-9:-1]) return self.get_hand_shake1() else: self.header['Version'] = 2 self.header['Token'] = self.get_token2(self.header['Sec-WebSocket-Key']) return self.get_hand_shake2() |
这里用python的socketServer模块搭建一个socket服务来说明这个类的用法:
View Code PYTHON
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler): '''线程请求管理''' def setup(self): self.request.settimeout(3600) #保证连接超时释放 self.web_socket = WebSocket(['chat', 'texas']) def handle(self): try: while True: ctime = time.time() buf = self.request.recv(1024) if not buf: break if buf[0:20] == 'GET /socket HTTP/1.1': data = self.web_socket.decode_header(buf[21:]) self.request.send(data) print data else: data = self.web_socket.unpack_data(buf) print '%s>>%s' % (data, binascii.hexlify(buf)) self.request.send(self.web_socket.pack_data('server return:%s' % data)) except socket.timeout:#连接超时 cur_thread = threading.currentThread() emsg = cur_thread.getName() + ' timeout connect!' print emsg except: print 'error in ThreadedTCPRequestHandler :%s' % traceback.format_exc() print 'request closed!' self.request.close() class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): pass class Server: def __init__(self, host, port): self.host = host self.port = port self.server = None ThreadedTCPServer.allow_reuse_address = True def run(self): self.server = ThreadedTCPServer((self.host, self.port), ThreadedTCPRequestHandler) self.server.serve_forever() if __name__ == '__main__': Server('0.0.0.0', 25525).run() |
前端html写作方法,没有什么特别注意的,websocket提供的api非常简单(ie9不支持,ie10支持,但需要win8才能安装,firefox支持很好,chrome支持,opera最新版不支持):
View Code HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | <html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>测试websocket</title>
<style>
.debug{ border:1px #333333 solid; width:80%; padding:5px; box-shadow:1px 2px 1px 1px #999;}
.debug span{ color:#900; font-weight:bold;}
</style>
</head>
<body>
<div>
<input type="text" id="msg" /><input type="button" id="submit" value="发送" />
</div>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.min.js" language="javascript"></script>
<script>
var xprint = function(msg){
var debug = $('#debug');
if(debug.length<1){
$('body').before($('<div id="debug" class="debug"></div>'));
debug = $('#debug');
}
debug.html(debug.html()+msg+'<br />');
};
var socket = null;
var socket_connected = false;
var testSocket = function(host, protocols){
try{
if($.browser.msie){
alert('Internet Explorer dont support WebSocket!');
return false;
}else if($.browser.mozilla){
socket = new MozWebSocket(host, ['chat','texas']);
}else if($.browser.safari || $.browser.opera){
socket = new WebSocket(host, protocols);
}
socket.onopen = function(e){
socket_connected = true;
xprint('<span>【socket connected】</span>:'+e);
};
socket.onmessage = function(e){
xprint('<span>【receive msg】</span>:'+e.data);
};
socket.onerror = function(evt){
xprint('<span>【socket error】</span>:'+evt.target);
}
socket.onclose = function(e){
socket_connected = false;
xprint('<span>【server closed】</span>:'+e);
socket.close();
};
}catch(e){
alert(e);
}
};
var testChat = function(){
var msg = '';
$('#submit').click(function(){
msg = $('#msg').val();
if(msg == '') return false;
socket.send(msg);
xprint('<span>【send msg】</span>:'+msg);
$('#msg').val('');
});
};
~function(){
var host = 'ws://192.168.0.237:25525/socket';
var protocols = 'my_protocols';
testSocket(host, protocols);
testChat();
}();
</script>
</body>
</html> |
上一篇: 关于html5和css3的一些尝试
下一篇: 一些linux基本命令
