pythonで書かれたwebsocketサービス
7232 ワード
ページ上でバックグラウンドのshellプログラムを呼び出すためですが、このshellは実行時間が長いので、非同期でshellの出力を取得しますか?shellの実行が完了するのを待つ必要はありません.データをすべて出力しますか?
元のhttpプロトコルではこの要求を完了することはできないことを知っています.出力をテキストに更新してjsで問い合わせて取りに行かない限り、これは冗談ではありませんか.
内部のプログラムなので、当然このような要求があるプログラムは基本的に内部、あるいは小さなグループで使うので、websocketを試してみましょう
まずwebsocketプロトコルを検討します
まずは握手
元の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を読み、具体的なコードは以下の通りである.3.前回読み込んだmaskingは暗号化されているかどうかを判断するために使用されます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,)
もし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