Web クライアントとサーバー間の Ajax (http) に基づく一般的な通信方法は、短い接続と長いポーリングに分けられます。
短い接続: クライアントとサーバーが HTTP 操作を実行するたびに接続が確立され、タスクが完了すると接続が終了します。
ロング ポーリング: クライアントは、従来のポーリングと同様にサーバーにデータを要求します。ただし、サーバーにクライアントにすぐに返せるデータがない場合、空の結果はすぐには返されませんが、リクエストはデータが到着するまで待機します (または、適切なタイムアウト: ajax タイムアウト未満)。 、結果としてデータをクライアントに返します。
ロングポーリングメカニズムを次の図に示します。
2. Websocketの基本概念WebSocket は、HTML5 が提供し始めた、単一の TCP 接続上で全二重通信を行うためのプロトコルです。
WebSocket を使用すると、クライアントとサーバー間のデータ交換が簡単になり、サーバーがデータをクライアントにアクティブにプッシュできるようになります。 WebSocket API では、ブラウザとサーバーはハンドシェイクを完了するだけでよく、双方向データ送信のために両者の間に永続的な接続を直接作成できます。
WebSocket API では、ブラウザとサーバーはハンドシェイク アクションを実行するだけで、ブラウザとサーバーの間に高速チャネルが形成されます。データは両者間で直接送信できます。
現在、プッシュ技術を実装するために、多くの Web サイトで Ajax ポーリングが使用されています。ポーリングとは、ブラウザが特定の時間間隔 (1 秒ごとなど) でサーバーに HTTP リクエストを発行し、サーバーが最新のデータをクライアントのブラウザに返すことです。この従来のモデルには明らかな欠点があります。つまり、ブラウザはサーバーにリクエストを継続的に送信する必要があります。ただし、HTTP リクエストには長いヘッダーが含まれる可能性があり、実際の有効なデータはその一部にすぎない可能性があり、これは明らかに無駄です。大量の帯域幅とその他のリソース。
HTML5 で定義された WebSocket プロトコルは、サーバーのリソースと帯域幅をより効果的に節約し、よりリアルタイムな通信を可能にします。
ブラウザは、JavaScript を介して WebSocket 接続を確立するリクエストをサーバーに送信します。接続が確立されると、クライアントとサーバーは TCP 接続を通じてデータを直接交換できます。
Web Socket 接続を取得したら、send() メソッドを通じてサーバーにデータを送信し、onmessage イベントを通じてサーバーから返されたデータを受信できます。
3. Websocket ハンドシェイクの原則:Websocket のハンドシェイク原理は、次の手順に大別できます。
コードの実装:
importソケット,base64,hashlib#ソケット接続を作成します。 sock =ソケット.socket(socket.AF_INET,socket.SOCK_STREAM)#終了アドレスとポート番号をバインドします。 sock.bind(('127.0.0.1',9527)#リッスンします。 sock.listen (5)# クライアントソケットオブジェクト conn を取得します、アドレス = sock.accept()#クライアントの[ハンドシェイク]情報を取得します data = conn.recv(1024)print(data)def get_headers(data): リクエスト ヘッダーから Sec-WebSocket-Key に対応する値を削除し、 header_dict = {} header_str = data.decode を返します( utf8) for i in header_str.split(/r/n): if str(i).startswith(Sec-WebSocket-Key): return i.split(:)[1].strip()# Sec-WebSocket-Key に対応する値を取得します。 ws_key = get_headers(data)# マジック文字列マジック文字列は次のとおりです: 258EAFA5-E914-47DA-95CA-C5AB0DC85B11magic_string = '258EAFA5 - E914-47DA-95CA-C5AB0DC85B11'#スプライシングsocket_str = ws_key + magic_string# sha1暗号化socket_str_sha1 = hashlib.sha1(socket_str.encode(utf8)).digest()#base64暗号化socket_str_base64 =base64.b64encode(socket_str_sha1)# スプライシングレスポンスヘッダresponse_tpl = HTTP/1.1 101 スイッチングプロトコル/r/n / アップグレード:websocket/r/n / 接続: アップグレード/r/n / Sec-WebSocket-Accept: %s/r/n / WebSocket-Location: ws://127.0.0.1:9527/r /n/r/n % (socket_str_base64.decode(utf8))#サーバーは応答ヘッダーをクライアントに送信します conn.send(response_tpl.encode(utf8))# クライアントとサーバーは、True の間、ループでデータを送受信するための長い接続を確立します: msg = conn.recv(8096) print(メッセージ)
<!DOCTYPE html><html lang=ja><head> <meta charset=UTF-8> <title>タイトル</title></head><body></body><script type=text/javascript> ws = new WebSocket(ws://127.0.0.1:9527); ws.onmessage = function (ev) { console.log(ev)//データ受信用}</script></html>
クライアントによって開始された HTTP リクエストのリクエスト ヘッダーが添付されています。
b'GET /ws/ HTTP/1.1Host: 127.0.0.1:9527Connection: UpgradePragma: no-cacheCache-Control: no-cacheUser-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.3...Upgrade: websocketOrigin : http://localhost:63342Sec-WebSocket-Version: 13Accept-Encoding: gzip、deflate、brAccept-Language: zh-CN,zh;q=0.9Sec-WebSocket-Key: kJXuOKsrl3AR1KeFngRElQ==Sec-WebSocket-Extensions: permessage- client_max_window_bits をデフレートします。4. Websocket の暗号化と復号化の方法:
復号化方法:
# b'/x81/x87/x0e/xc3/xf3/xcd;/xf6/xc6/xf8;/xf6/xc6'==========5555555hashstr = b'/x81/x87/x0e/xc3 /xf3/xcd;/xf6/xc6/xf8;/xf6/xc6'# 2 番目のバイト (/x87 ビット 9 ~ 16、および 127 ペイロード = hashstr[1] & 127#) でビット演算を実行します。ビット演算の結果が 127 に等しい場合、3 ~ 10 バイトはデータ長です。バイト11 ~ 14 はマスク復号化に必要な文字列です # payload == 127 の場合、データは 15 バイト目から最後までです: extend_payload_len = hashstr[2:10] Mask = hashstr[10:14] decoded = hashstr[14:]# ビット演算結果が 126 の場合、3 ~ 4 バイトはデータ長です # 5 ~ 8 バイトはマスク復号化に必要な文字列です # 次に、データ ペイロード == 126 の場合、9 バイト目から最後まで: extend_payload_len = hashstr[2:4] マスク = hashstr[4:8] デコード = hashstr[8:]#ビット演算結果が 125 以下の場合、この数値がデータの長さになります # 3 ~ 6 バイトはマスク復号化に必要な文字列です # ペイロード <= の場合、データは 7 バイト目から最後までです125: extend_payload_len = なしマスク = hashstr[2:6] デコード = hashstr[6:]str_byte = bytearray()for i in range(len(decode)): byte = decoded[i] ^ Mask[i % 4] str_byte.append(byte)print(str_byte.decode(utf8))
暗号化方式:
import structmsg_bytes = 5555555.encode(utf8)token = b/x81length = len(msg_bytes)if 長さ < 126: トークン += struct.pack(B, length)elif 長さ == 126: トークン += struct.pack(!BH , 126, length)else: トークン += struct.pack(!BQ, 127, length)msg = トークン + msg_bytesprint(msg)4. flask フレームワークと Websocket プロトコルに基づくクライアントとサーバーのリンク通信の例:
pip3 install gevent-websocket
from flask import Flask, requestfrom geventwebsocket.websocket import WebSocketfrom gevent.pywsgi import WSGIServerfrom geventwebsocket.handler import WebSocketHandlerapp = Flask(__name__)@app.route(/ws)def websocket(): # ユーザーのリンク user_socket = request.environ を取得します。 get(wsgi.websocket) # type:WebSocket print (アクセス成功) while True: msg = user_socket.receive() #メッセージを受信 print(msg) user_socket.send(msg) #メッセージを送信 if __name__ == '__main__': # アドレスとポート番号を指定して、 Websocket サービスを開く http_serv = WSGIServer((127.0.0.1, 8001), app, handler_class=WebSocketHandler) # Websocket サービスを開始します http_serv.serve_forever()
htmlファイル:
<!DOCTYPE html><html lang=ja><head> <meta charset=UTF-8> <title>タイトル</title> <link href=https://cdnjs.cloudflare.com/ajax/libs/twitter- bootstrap/3.3.7/css/bootstrap.css rel=stylesheet></head><body><botton class=btn btn-default onclick=createsocket()>クリックしてリンクを作成します</botton><br><p>メッセージを入力してください: <input type=text placeholder=入力メッセージ id=msg></p><buttom class=btn btn-成功 onclick =send_msg()>メッセージの送信</buttom><script> var ws = null function createdsocket() { ws = new; WebSocket(ws://127.0.0.1:8001/ws); ws.onmessage = function (data) { console.log(サーバーから受信したメッセージ=,data.data) } } function send_msg() { var to_msg = document.getElementById(msg).value; ws.send(to_msg) }</script></body></html>
クライアント.png
サーバー側.png
このようにして、Websocket プロトコルを介したクライアント/サーバー通信を実装するだけです。また、複数のリンクを作成して、同時にサーバー側と通信することもできます。
5. Websocket に基づいてインスタント メッセージング (IM) を実装します。サーバーコード:
from flask import Flask、requestfrom geventwebsocket.websocket import WebSocketfrom gevent.pywsgi import WSGIServerfrom geventwebsocket.handler import WebSocketHandlerfrom geventwebsocket.Exceptions import WebSocketErrorimport jsonapp = Flask(__name__)user_socket_dict = {}@app.route(/ws/<username>)def websocket(username): # ユーザーのリンクを取得します user_socket = request.environ.get(wsgi.websocket) # type:WebSocket user_socket_dict[username] = user_socket print(username+link success!) while True: msg = user_socket.receive() # user_socket_dict.values() でソケットのメッセージを受け入れる: # type:WebSocket if user_socket !=ソケット:# 自分でメッセージを送信した場合、サーバーは次の場合を除き、応答しません: continueif __name__ == '__main__ ': # Websocket サービスを開くためのアドレスとポート番号を指定します http_serv = WSGIServer((127.0.0.1, 8001), app, handler_class=WebSocketHandler) # Websocket サービスを開始します http_serv.serve_forever()
htmlコード:
<!DOCTYPE html><html lang=ja><head> <meta charset=UTF-8> <title>タイトル</title> <link href=https://cdnjs.cloudflare.com/ajax/libs/twitter- bootstrap/3.3.7/css/bootstrap.css rel=stylesheet></head><body><p>ニックネームを入力してください:<input type=text id=username></p><botton class=btn btn-default onclick=createsocket()>クリックしてリンクを作成します</botton><br><p>メッセージを入力してください: <input type=text id=msg ></p><buttom class=btn btn-success onclick=send_msg()>メッセージを送信</buttom><br><br><br><div style=border: 2pxソリッド; 幅: 500px; 高さ: 800px; id=text_div></div><script> var ws = null; var username = null; WebSocket(ws://127.0.0.1:8001/ws + / + ユーザー名); (データ) { var text_div = document.getElementById(text_div); var obj_data = JSON.parse(data.data); var add_msg = <p> + obj_data.sender + : + obj_data.msg + </p>; innerHTML += add_msg; } } 関数 send_msg() { var to_msg = document.getElementById(msg).value; var text_div = document.getElementById(text_div); var add_msg = <p style='text-align: right'> + to_msg + : + text_div.innerHTML + = add_msg; }</script></body></html>
クライアント01.png
クライアント02.png
サーバー側.png
このコードはデモコードであり、バグやバグが含まれています。現時点では主に学習用に使用されているため、細かいことは許可されません。興味のある方はさらに最適化できます! ! !
以上がこの記事の全内容です。皆様の学習のお役に立てれば幸いです。また、VeVb Wulin Network をご支援いただければ幸いです。