웹 클라이언트와 서버 간의 Ajax(http) 기반의 일반적인 통신 방법은 짧은 연결 과 긴 폴링 으로 구분됩니다.
짧은 연결: 클라이언트와 서버가 HTTP 작업을 수행할 때마다 연결이 설정되고 작업이 완료되면 연결이 종료됩니다.
긴 폴링: 클라이언트는 기존 폴링과 마찬가지로 서버에 데이터를 요청합니다. 그러나 서버에 클라이언트에 즉시 반환할 수 있는 데이터가 없으면 빈 결과를 즉시 반환하지 않고 데이터가 도착할 때까지 요청을 계속 대기합니다(또는 적절한 제한 시간: ajax 제한 시간보다 작음). , 결과로 데이터를 반환합니다.
긴 폴링 메커니즘은 아래 그림에 나와 있습니다.
2. 웹소켓의 기본 개념WebSocket은 HTML5가 제공하기 시작한 단일 TCP 연결에서 전이중 통신을 위한 프로토콜입니다.
WebSocket은 클라이언트와 서버 간의 데이터 교환을 더 간단하게 만들어 서버가 클라이언트에 데이터를 적극적으로 푸시할 수 있도록 합니다. WebSocket API에서는 브라우저와 서버가 핸드셰이크만 완료하면 되며, 양방향 데이터 전송을 위해 둘 사이에 직접 지속적인 연결이 생성될 수 있습니다.
WebSocket API에서는 브라우저와 서버가 핸드셰이크 작업만 수행하면 브라우저와 서버 사이에 빠른 채널이 형성됩니다. 데이터는 둘 사이에서 직접 전송될 수 있습니다.
요즘에는 푸시 기술을 구현하기 위해 많은 웹사이트에서 Ajax 폴링을 사용합니다. 폴링은 브라우저가 특정 시간 간격(예: 1초마다)으로 서버에 HTTP 요청을 보낸 다음 서버가 클라이언트의 브라우저에 최신 데이터를 반환하는 것입니다. 이 전통적인 모델은 분명한 단점을 가지고 있습니다. 즉, 브라우저는 서버에 지속적으로 요청을 보내야 합니다. 그러나 HTTP 요청에는 실제 유효한 데이터가 작은 부분에 불과할 수 있는 긴 헤더가 포함될 수 있으며 이는 분명히 낭비입니다. . 많은 대역폭 및 기타 리소스.
HTML5에 의해 정의된 WebSocket 프로토콜은 서버 리소스와 대역폭을 더 효과적으로 절약하고 더 많은 실시간 통신을 가능하게 합니다.
브라우저는 JavaScript를 통해 WebSocket 연결을 요청을 서버에 보냅니다. 연결이 설정된 후 클라이언트와 서버는 TCP 연결을 통해 직접 데이터를 교환할 수 있습니다.
웹 소켓 연결을 얻은 후 send() 메서드를 통해 서버에 데이터를 보내고, onmessage 이벤트를 통해 서버에서 반환된 데이터를 받을 수 있습니다.
3. 웹소켓 핸드셰이크 원리:Websocket의 핸드셰이크 원리는 대략 다음 단계로 나눌 수 있습니다.
코드 구현:
importsocket, base64, hashlib# 소켓 연결 생성 sock =socket.socket(socket.AF_INET,socket.SOCK_STREAM)# 끝 주소와 포트 번호 바인딩 sock.bind(('127.0.0.1', 9527))# 듣기 sock.listen (5)# 클라이언트 소켓 개체 conn을 가져옵니다. 주소 = sock.accept()# 클라이언트의 [handshake] 정보 가져오기 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'# 스플라이싱 소켓_str = ws_key + Magic_string# sha1 암호화 소켓_str_sha1 = hashlib.sha1(socket_str.encode(utf8)).digest()# base64 암호화 소켓_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-위치: ws://127.0.0.1:9527/r /n/r/n % (socket_str_base64.decode(utf8))# 서버는 클라이언트에 응답 헤더를 보냅니다. conn.send(response_tpl.encode(utf8))# 클라이언트와 서버는 루프에서 데이터를 주고 받기 위해 긴 연결을 설정합니다. while True: msg = conn.recv(8096) print( 메시지)
<!DOCTYPE html><html lang=en><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...업그레이드: websocketOrigin : http://localhost:63342Sec-WebSocket-버전: 13Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9Sec-WebSocket-Key: kJXuOKsrl3AR1KeFngRElQ==Sec-WebSocket-Extensions: permessage- 수축; client_max_window_bits'4. 웹소켓 암호화 및 복호화 방법:
해독 방법:
# 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'# 두 번째 바이트인 /x87 비트 9~16, 127에 대해 비트 연산을 수행합니다. payload = 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바이트는 마스크 복호화에 필요한 문자열입니다. # 그러면 data payload == 126인 경우 9번째 바이트부터 끝까지: extend_payload_len = hashstr[2:4] 마스크 = hashstr[4:8] decoded = hashstr[8:]# 비트 연산 결과가 125 이하인 경우 이 숫자는 데이터의 길이입니다. # 3~6바이트는 마스크 복호화에 필요한 문자열입니다. # 페이로드 <=인 경우 데이터는 7번째 바이트부터 끝까지입니다. 125: extend_payload_len = 없음 마스크 = hashstr[2:6] decoded = hashstr[6:]str_byte = byteearray()for i in range(len(decoded)): byte = decoded[i] ^ 마스크[i % 4] str_byte.append(byte)print(str_byte.decode(utf8))
암호화 방법:
import structmsg_bytes = 5555555.encode(utf8)token = b/x81length = len(msg_bytes)if length < 126: 토큰 += struct.pack(B, length)elif length == 126: token += struct.pack(!BH , 126, 길이)else: 토큰 += struct.pack(!BQ, 127, 길이)msg = 토큰 + msg_bytesprint(msg)4. 플라스크 프레임워크와 웹소켓 프로토콜을 기반으로 한 클라이언트 및 서버 링크 통신의 예:
pip3 install gevent-websocket
from 플라스크 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) # 웹소켓 서비스 시작 http_serv.serve_forever()
HTML 파일:
<!DOCTYPE html><html lang=en><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 createsocket() { 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, 요청 from geventwebsocket.websocket 가져오기 WebSocketfrom gevent.pywsgi 가져오기 WSGIServerfrom geventwebsocket.handler 가져오기 WebSocketHandlerfrom geventwebsocket.Exceptions 가져오기 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 성공!) while True: msg = user_socket.receive() # user_socket_dict.values()에서 소켓에 대한 메시지를 수락합니다: # type:WebSocket if user_socket != 소켓:# 메시지를 직접 보내면 서버가 응답하지 않습니다. 다음을 시도해 보세요. 소켓.send(json.dumps({sender: 사용자 이름, msg: msg})) 제외: continueif __name__ == '__main__ ': # 웹소켓 서비스를 열기 위한 주소와 포트 번호를 지정합니다. http_serv = WSGIServer((127.0.0.1, 8001), app, handler_class=WebSocketHandler) # 웹소켓 서비스 시작 http_serv.serve_forever()
HTML 코드:
<!DOCTYPE html><html lang=en><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; function createsocket() { 사용자 이름 = document.getElementById(username).value; WebSocket(ws://127.0.0.1:8001/ws + / + 사용자 이름) ws.onmessage = 함수 (데이터) { 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; ws.send(to_msg) }</script></body></html>
Client01.png
클라이언트02.png
서버측.png
코드는 데모용 코드로, 버그와 버그가 있어 현재 주로 학습용으로 사용되므로 얄짤은 허용되지 않습니다. 관심있는 분들은 더욱 최적화 할 수 있습니다! ! !
위 내용은 이 기사의 전체 내용입니다. 모든 분들의 학습에 도움이 되기를 바랍니다. 또한 모든 분들이 VeVb Wulin Network를 지지해 주시길 바랍니다.