[Python]通过websocket与js客户端通信

来源:互联网 发布:淘宝上化妆品是真的吗 编辑:程序博客网 时间:2024/05/23 15:15

网站大多使用HTTP协议通信,而HTTP是无连接的协议。只有客户端请求时,服务器端才能发出相应的应答,HTTP请求的包也比较大,如果只是很小的数据通信,开销过大。于是,我们可以使用websocket这个协议,用最小的开销实现面向连接的通信。

具体的websocket介绍可见http://zh.wikipedia.org/wiki/WebSocket 

这里,介绍如何使用Python与前端js进行通信。

 

websocket使用HTTP协议完成握手之后,不通过HTTP直接进行websocket通信。

于是,使用websocket大致两个步骤:使用HTTP握手,通信。

 

js处理websocket要使用ws模块;Python处理则使用socket模块建立TCP连接即可,比一般的socket,只多一个握手以及数据处理的步骤。

握手


过程

 

包格式

js客户端先向服务器端python发送握手包,格式如下:

GET /chat HTTP/1.1Host: server.example.comUpgrade: websocketConnection: UpgradeSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==Origin: http://example.comSec-WebSocket-Protocol: chat, superchatSec-WebSocket-Version: 13

服务器回应包格式:

HTTP/1.1 101 Switching ProtocolsUpgrade: websocketConnection: UpgradeSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=Sec-WebSocket-Protocol: chat

其中,Sec-WebSocket-Key是随机的,服务器用这些数据构造一个SHA-1信息摘要。

方法为:key+migicSHA-1 加密,base-64加密,如下:

 

Python中的处理代码

MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'res_key = base64.b64encode(hashlib.sha1(sec_key + MAGIC_STRING).digest())

握手完整代码

js

js中有处理websocket的类,初始化后自动发送握手包,如下:

var socket = new WebSocket('ws://localhost:3368'); 

Python

Pythonsocket接受得到握手字符串,处理后发送

HOST = 'localhost'PORT = 3368MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'HANDSHAKE_STRING = "HTTP/1.1 101 Switching Protocols\r\n" \                   "Upgrade:websocket\r\n" \                   "Connection: Upgrade\r\n" \                   "Sec-WebSocket-Accept: {1}\r\n" \                   "WebSocket-Location: ws://{2}/chat\r\n" \                   "WebSocket-Protocol:chat\r\n\r\n" def handshake(con):#con为用socket,accept()得到的socket#这里省略监听,accept的代码,具体可见blog:http://blog.csdn.net/ice110956/article/details/29830627    headers = {}    shake = con.recv(1024)     if not len(shake):        return False     header, data = shake.split('\r\n\r\n', 1)    for line in header.split('\r\n')[1:]:        key, val = line.split(': ', 1)        headers[key] = val     if 'Sec-WebSocket-Key' not in headers:        print ('This socket is not websocket, client close.')        con.close()        return False     sec_key = headers['Sec-WebSocket-Key']    res_key = base64.b64encode(hashlib.sha1(sec_key + MAGIC_STRING).digest())     str_handshake = HANDSHAKE_STRING.replace('{1}', res_key).replace('{2}', HOST + ':' + str(PORT))    print str_handshake    con.send(str_handshake)return True

通信

不同版本的浏览器定义的数据帧格式不同,Python发送和接收时都要处理得到符合格式的数据包,才能通信。

Python接收

Python接收到浏览器发来的数据,要解析后才能得到其中的有用数据。

浏览器包格式

 

固定字节:

1000 0001或是1000 0002)这里没用,忽略

包长度字节:

第一位肯定是1,忽略。剩下7个位可以得到一个整数(0 ~ 127),其中

1-125)表此字节为长度字节,大小即为长度;

(126)表接下来的两个字节才是长度;

(127)表接下来的八个字节才是长度;

用这种变长的方式表示数据长度,节省数据位。

mark掩码:

mark掩码为包长之后的4个字节,之后的兄弟数据要与mark掩码做运算才能得到真实的数据。

兄弟数据:

得到真实数据的方法:将兄弟数据的每一位x,和掩码的第i%4位做xor运算,其中ix在兄弟数据中的索引。

完整代码

def recv_data(self, num):    try:        all_data = self.con.recv(num)        if not len(all_data):            return False    except:        return False    else:        code_len = ord(all_data[1]) & 127        if code_len == 126:            masks = all_data[4:8]            data = all_data[8:]        elif code_len == 127:            masks = all_data[10:14]            data = all_data[14:]        else:            masks = all_data[2:6]            data = all_data[6:]        raw_str = ""        i = 0        for d in data:            raw_str += chr(ord(d) ^ ord(masks[i % 4]))            i += 1        return raw_str

js端的ws对象,通过ws.send(str)即可发送

ws.send(str)

Python发送

Python要包数据发送,也需要处理,发送包格式如下

 

固定字节:固定的1000 0001(\x81)

包长:根据发送数据长度是否超过1250xFFFF(65535)来生成1个或3个或9个字节,来代表数据长度。

def send_data(self, data):    if data:        data = str(data)    else:        return False    token = "\x81"    length = len(data)    if length < 126:        token += struct.pack("B", length)    elif length <= 0xFFFF:        token += struct.pack("!BH", 126, length)    else:        token += struct.pack("!BQ", 127, length)    #struct为Python中处理二进制数的模块,二进制流为C,或网络流的形式。    data = '%s%s' % (token, data)    self.con.send(data)    return True

js端通过回调函数ws.onmessage()接受数据

ws.onmessage = function(result,nTime){alert("从服务端收到的数据:");alert("最近一次发送数据到现在接收一共使用时间:" + nTime);console.log(result);}

最终代码


Python服务端

# _*_ coding:utf-8 _*___author__ = 'Patrick'  import socketimport threadingimport sysimport osimport MySQLdbimport base64import hashlibimport struct # ====== config ======HOST = 'localhost'PORT = 3368MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'HANDSHAKE_STRING = "HTTP/1.1 101 Switching Protocols\r\n" \                   "Upgrade:websocket\r\n" \                   "Connection: Upgrade\r\n" \                   "Sec-WebSocket-Accept: {1}\r\n" \                   "WebSocket-Location: ws://{2}/chat\r\n" \                   "WebSocket-Protocol:chat\r\n\r\n" class Th(threading.Thread):    def __init__(self, connection,):        threading.Thread.__init__(self)        self.con = connection     def run(self):        while True:            try:               pass        self.con.close()     def recv_data(self, num):        try:            all_data = self.con.recv(num)            if not len(all_data):                return False        except:            return False        else:            code_len = ord(all_data[1]) & 127            if code_len == 126:                masks = all_data[4:8]                data = all_data[8:]            elif code_len == 127:                masks = all_data[10:14]                data = all_data[14:]            else:                masks = all_data[2:6]                data = all_data[6:]            raw_str = ""            i = 0            for d in data:                raw_str += chr(ord(d) ^ ord(masks[i % 4]))                i += 1            return raw_str     # send data    def send_data(self, data):        if data:            data = str(data)        else:            return False        token = "\x81"        length = len(data)        if length < 126:            token += struct.pack("B", length)        elif length <= 0xFFFF:            token += struct.pack("!BH", 126, length)        else:            token += struct.pack("!BQ", 127, length)        #struct为Python中处理二进制数的模块,二进制流为C,或网络流的形式。        data = '%s%s' % (token, data)        self.con.send(data)        return True      # handshake    def handshake(con):        headers = {}        shake = con.recv(1024)         if not len(shake):            return False         header, data = shake.split('\r\n\r\n', 1)        for line in header.split('\r\n')[1:]:            key, val = line.split(': ', 1)            headers[key] = val         if 'Sec-WebSocket-Key' not in headers:            print ('This socket is not websocket, client close.')            con.close()            return False         sec_key = headers['Sec-WebSocket-Key']        res_key = base64.b64encode(hashlib.sha1(sec_key + MAGIC_STRING).digest())         str_handshake = HANDSHAKE_STRING.replace('{1}', res_key).replace('{2}', HOST + ':' + str(PORT))        print str_handshake        con.send(str_handshake)        return True def new_service():    """start a service socket and listen    when coms a connection, start a new thread to handle it"""     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    try:        sock.bind(('localhost', 3368))        sock.listen(1000)        #链接队列大小        print "bind 3368,ready to use"    except:        print("Server is already running,quit")        sys.exit()     while True:        connection, address = sock.accept()        #返回元组(socket,add),accept调用时会进入waite状态        print "Got connection from ", address        if handshake(connection):            print "handshake success"            try:                t = Th(connection, layout)                t.start()                print 'new thread for client ...'            except:                print 'start new thread error'                connection.close()  if __name__ == '__main__':    new_service()

js客户

<script>var socket = new WebSocket('ws://localhost:3368');ws.onmessage = function(result,nTime){alert("从服务端收到的数据:");alert("最近一次发送数据到现在接收一共使用时间:" + nTime);console.log(result);}</script>

推荐blog

http://blog.csdn.net/fenglibing/article/details/7108982 

http://blog.mycolorway.com/2011/11/22/a-minimal-python-websocket-server/ 

1 0
原创粉丝点击