Analysis of communication examples between Python and js clients through websocket

Source: Internet
Author: User
Most websites use HTTP Communication, while HTTP is a connectionless protocol. Only when the client requests, the server can send a corresponding response, and the HTTP request package is also relatively large. If it is only a small data communication, the overhead is too large. So, we can use websocket this specific websocket introduction visible http://zh.wikipedia.org/wiki/WebSocket

This section describes how to use Python to communicate with front-end js.

After websocket uses the HTTP protocol to complete handshake, it does not directly communicate with websocket through HTTP.

Therefore, websocket has two steps: HTTP handshake and communication.

Js uses the ws module to process websocket; Python uses the socket module to establish a TCP connection. Compared with a general socket, there is only one handshake and data processing step.

Handshake

Process

Package format

The js client sends a handshake packet to the python server in the following format:

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

Server response package format:

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

The Sec-WebSocket-Key is random, and the server uses the data to construct a SHA-1 information digest.

The key + migic, SHA-1, and base-64 encryption methods are as follows:

Processing code in Python

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

Complete handshake code

Js end

Js contains a class for processing websocket. After initialization, the handshake package is automatically sent, as shown below:

Var socket = new WebSocket ('ws: // localhost: 100 ');

Python end

Python uses socket to receive a handshake string, which is sent after processing

HOST = 'localhost' PORT = 3366magic_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 "def handshake (con): # con uses socket, socket obtained by accept () # The listening and accept code is omitted here. For details, see 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

Communication

Different Versions of browsers define different data frame formats. When sending and receiving data packets in the correct format, Python must process the data packets before communication.

Python receiving

Python receives the data sent from the browser and can obtain useful data only after parsing.

Browser package format

Fixed byte:

(1000 0001 or 1000 0002) This is useless. Ignore it.

Packet Length Byte:

The first one must be 1. Ignore it. The remaining seven digits can get an INTEGER (0 ~ 127), where

(1-125) the size of this byte is the length;

(126) The next two bytes in the table are the length;

(127) the next eight bytes of the table are the length;

This variable length method is used to indicate the Data Length, saving the data bit.

Mark mask:

The mark mask is the four bytes after the package length, and the subsequent sibling data must be computed with the mark mask to obtain real data.

Brother data:

To obtain the real data, perform the xor operation on each x of the sibling data and the I % 4 of the mask, where I is the index of x in the sibling data.

Complete code

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-side ws object, which can be sent through ws. send (str)

Ws. send (str)

Send in Python

Python also needs to process packet data transmission. The format of the package is as follows:

Fixed byte: Fixed 1000 0001 ('\ x81 ′)

Package length: 1 or 3 or 9 bytes are generated based on whether the length of the sent data exceeds 125, and 0 xFFFF (65535) to indicate the Data Length.

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 <= 0 xFFFF: token + = struct. pack ("! BH ", 126, length) else: token + = struct. pack ("! BQ ", 127, length) # struct is a module for processing binary numbers in Python, And the binary stream is in the form of a C or network stream. Data = '% s % s' % (token, data) self. con. send (data) return True

Js receives data through the callback function ws. onmessage ()

Ws. onmessage = function (result, nTime) {alert ("data received from the server:"); alert ("Total time used to receive data from the last time:" + nTime ); console. log (result );}

Final code

Python Server

# _ * _ Coding: UTF-8 _ * ___ author _ = 'Patrick 'import socketimport threadingimport sysimport osimport MySQLdbimport base64import hashlibimport struct #===== config === HOST = 'localhost' PORT = 3366magic_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 "\" W EbSocket-Location: ws: // {2}/chat \ r \ n "\" WebSocket-Protocol: chat \ 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 else t: return False else: code_len = ord (all_data [1]) & 127 If code_len = 126: masks = all_data [] data = all_data [8:] elif code_len = 127: masks = all_data [] 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 len Token <126: token + = struct. pack ("B", length) elif length <= 0 xFFFF: token + = struct. pack ("! BH ", 126, length) else: token + = struct. pack ("! BQ ", 127, length) # struct is a module for processing binary numbers in Python, And the binary stream is in the form of a C or network stream. 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) # Link queue size print "bind 3368, ready to use" bytes T: print ("Server is already running, quit") sys. exit () while True: connection, address = sock. accept () # The returned tuples (socket, add). When the accept is called, it enters the waite status print "Got connection from", address if handshake (connection): print "handshake success" try: t = Th (connection, layout) t. start () print 'new thread for client... 'failed T: print 'start new thread error' connection. close () if _ name _ = '_ main _': new_service ()

Js Client

Script var socket = new WebSocket ('ws: // localhost: 100'); ws. onmessage = function (result, nTime) {alert ("data received from the server:"); alert ("Total time used to receive data from the last time:" + nTime ); console. log (result);} script

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.