pythonで書かれたwebsocketサービス


ページ上でバックグラウンドのshellプログラムを呼び出すためですが、このshellは実行時間が長いので、非同期でshellの出力を取得しますか?shellの実行が完了するのを待つ必要はありません.データをすべて出力しますか?
元のhttpプロトコルではこの要求を完了することはできないことを知っています.出力をテキストに更新してjsで問い合わせて取りに行かない限り、これは冗談ではありませんか.
内部のプログラムなので、当然このような要求があるプログラムは基本的に内部、あるいは小さなグループで使うので、websocketを試してみましょう
まずwebsocketプロトコルを検討します
まずは握手
        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 
Sec-WebSocket-Accept
 前者是客户端,也就是客户端生成的,那么后者咋生成咧 
   
  

公式如下

Sec-WebSocket-Accept = base64(sha1(Sec-WebSocket-Key+258EAFA5-E914-47DA-95CA-C5AB0DC85B11))

「+」号は含まれていません
具体的なpythonコードは以下の通りです.
# -*- coding: utf8 -*-

import socket
import time
from threading import Thread
import hashlib
import base64
class returnCrossDomain(Thread):
    def __init__(self,connection):
        Thread.__init__(self)
        self.con = connection
        self.isHandleShake = False
    def run(self):
        while True:
            if not self.isHandleShake: #  
                clientData  = self.con.recv(1024)
                dataList = clientData.split("\r
") header = {} print clientData for data in dataList: if ": " in data: unit = data.split(": ") header[unit[0]] = unit[1] secKey = header['Sec-WebSocket-Key']; resKey = base64.encodestring(hashlib.new("sha1",secKey+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest()).replace('
',''); response = '''HTTP/1.1 101 Switching Protocols\r
''' response += '''Upgrade: websocket\r
''' response += '''Connection: Upgrade\r
''' response += '''Sec-WebSocket-Accept: %s\r
'''%(resKey,) response += '''Sec-WebSocket-Protocol: chat\r
\r
''' self.con.send(response) self.isHandleShake = True else: data = self.con.recv(1024) print data def main(): sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.bind(('',88)) sock.listen(100) while True: try: connection,address = sock.accept() returnCrossDomain(connection).start() except: time.sleep(1) if __name__=="__main__": main()

注意pythonのbase 64.encodestringは自動的に処理する必要があります
jsコードは以下の通りです.
var socket = new WebSocket('ws://localhost:88');
socket.onopen = function() {
        alert(1);
}

スムーズにポップアップ1
下からデータの伝送を研究して1つの問題が私を振り回して長い間、私が複製する時この頭を取り除いていないため、ブラウザがずっと情報を受け取ることができなくて、私は憂鬱です
response += '''Sec-WebSocket-Protocol: chat\r
\r
'''

この言葉の意味は、chatというプロトコルを使って、websocketプロトコルを使わないので、私はwebsocketプロトコルに従って、どうしてもデータを受け取ることができなくて、取り除いてから、断固として成功しました.
Websocketのデータ転送部分は少し面倒で、古いバージョンのwebsocketは簡単で、すべてのメッセージは0 xFFの先頭で、0 x 00の終わりで、簡単ですが、安全問題を引き起こすに違いありません.
新しいバージョンのプロトコルは次のとおりです.
サービス側がクライアントからデータを読み込む
1.1 Byteバイトを読んで、注意はByte=8 bits、これは注意して、おばあちゃんの、説明文書を読んだばかりの時、見たことがあって、どうしても読めません
この1つのByteは8つのbitに分けられ、最初の4つのbitは研究されず、後の4つのbitはこのデータセグメントの役割を表し、例えば1つのデータ、1つのsocketが情報を閉じるなど、
この4バイトを取得するのは簡単です
data_head = self.con.recv(1)
header = struct.unpack("B",data_head)[0]
opcode = header & 0b00001111

具体的なopcodeの定義は以下の通りです.
      *  %x0 denotes a continuation frame

      *  %x1 denotes a text frame

      *  %x2 denotes a binary frame

      *  %x3-7 are reserved for further non-control frames

      *  %x8 denotes a connection close

      *  %x9 denotes a ping

      *  %xA denotes a pong

      *  %xB-F are reserved for further control frames

2.さらに1個byteを読み込むbyte=8個bits
この8つのbitsは両端に分かれていて、最初のbitはmaskingかどうかを表しています.私の理解は暗号化されているかどうかです.
後7つのbitsは真のデータの長さpayloadlengthを表し、payloadlength<=125であればpayloadlengthであり、payloadlength=126であれば2 Bytesのunsiged integerに行ってデータの長さを表し、payloadlength=127であれば8バイトのunsigned longを読み、具体的なコードは以下の通りである.
data_length = self.con.recv(1)
data_lengths= struct.unpack("B",data_length)
data_length = data_lengths[0]& 0b01111111
print bin(data_lengths[0])
masking = data_lengths[0] >> 7

if data_length<=125:
     payloadLength = data_length
elif data_length==126:
     payloadLength = struct.unpack("H",self.con.recv(2))[0]
elif data_length==127:
     payloadLength = struct.unpack("Q",self.con.recv(8))[0]
print "      :%d"%(data_length,)
3.前回読み込んだmaskingは暗号化されているかどうかを判断するために使用されます
もしmasking==1
では4つのBytesを読みます
次に長さに従ってデータを読み出し、復号作業を行い、具体的なコードとアルゴリズムは以下の通りである.
masking=0の場合
それでは先ほど計算したデータ長のデータをそのまま読み込みます
                   if masking==1:
                        print " masking"
                        maskingKey = self.con.recv(4)
                        self.maskingKey = maskingKey
                    data = self.con.recv(payloadLength)
                    i = 0
                    true_data = ''
                    for d in data:
                        true_data += chr(ord(d) ^ ord(maskingKey[i%4]))
                        i += 1
                    self.onData(true_data)

OKクライアントのデータの読み取りが完了し、下に送信データがあります
データの送信は簡単で、受信データと同じフォーマットですが、maskingで暗号化する必要はありませんので、2バイト目の最初のbitを0に設定します.
具体的なコードは以下の通りです.
def sendData(self,text) :
        print "        %s"%(text,)
        # 
        self.con.send(struct.pack("!B",0x81))
        #    
        length = len(text)
       # masking = 0b00000000;

        if length<=125:
            self.con.send(struct.pack("!B",length))

        elif length<=65536:
            self.con.send(struct.pack("!B",126))
            self.con.send(struct.pack("!H",length))
        else:
            self.con.send(struct.pack("!B",127))
            self.con.send(struct.pack("!Q",length))

        self.con.send(struct.pack("!%ds"%(length,),text))

具体的なコードは私のgitの上でダウンロードすることができて、私はすでにモジュールをカプセル化して、対応するjsも1つの书いたクラスがあって、中にdemoがあってみんなを歓迎して试みます
https://github.com/suxianbaozi/pywebsocketserver