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

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

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

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

握手

过程


python 开启snmp python 开启端口与JavaScript交互_Python

包格式

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

 GET /chat HTTP/1.1 
        
 
         Host: server.example.com 
        
 
         Upgrade: websocket 
        
 
         Connection: Upgrade 
        
 
         Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== 
        
 
         Origin:http://example.com 
        
 
         Sec-WebSocket-Protocol: chat, superchat 
        
 
         Sec-WebSocket-Version: 13 
        


服务器回应包格式:


 HTTP/1.1 101 Switching Protocols 
        
 
         Upgrade: websocket 
        
 
         Connection: Upgrade 
        
 
         Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= 
        
 
         Sec-WebSocket-Protocol: chat 


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

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


python 开启snmp python 开启端口与JavaScript交互_HTTP_02




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 端

Python 用 socket 接受得到握手字符串,处理后发送

 HOST 
         = 
         'localhost' 
        
 
         PORT 
         = 
         3368 
        
 
         MAGIC_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 接收到浏览器发来的数据,要解析后才能得到其中的有用数据。

浏览器包格式


python 开启snmp python 开启端口与JavaScript交互_Python_03

固定字节:

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

包长度字节:

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

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

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

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

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

mark 掩码:

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

兄弟数据:

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

完整代码


 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 要包数据发送,也需要处理,发送包格式如下


python 开启snmp python 开启端口与JavaScript交互_HTTP_04

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

包长:根据发送数据长度是否超过 125 , 0xFFFF(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 
         socket 
        
 
         import 
         threading 
        
 
         import 
         sys 
        
 
         import 
         os 
        
 
         import 
         MySQLdb 
        
 
         import 
         base64 
        
 
         import 
         hashlib 
        
 
         import 
         struct 
        
 
            
        
 
         # ====== config ====== 
        
 
         HOST 
         = 
         'localhost' 
        
 
         PORT 
         = 
         3368 
        
 
         MAGIC_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客户 端

?

         1 
       

         2 
       

         3 
       

         4 
       

         5 
       

         6 
       

         7 
       

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