请稍等...页面即将加载完成

关于websocket的python实现

时间: 2012-01-16 / 分类: Ajax应用 / 浏览次数: 142 views / 0个评论 发表评论

      又是闲来无事,熟悉一下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最新版不支持):

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>
更多
订阅

发表评论

您的昵称 *

您的邮箱 *

您的网站


five + 8 =