วิธีการสื่อสารทั่วไปที่ใช้ Ajax (http) ระหว่างเว็บไคลเอนต์และเซิร์ฟเวอร์แบ่งออกเป็นการ เชื่อมต่อแบบสั้น และ การโพลแบบยาว
การเชื่อมต่อแบบสั้น: ทุกครั้งที่ไคลเอนต์และเซิร์ฟเวอร์ดำเนินการ HTTP การเชื่อมต่อจะถูกสร้างขึ้น และการเชื่อมต่อจะสิ้นสุดลงเมื่องานเสร็จสมบูรณ์
การโพลแบบยาว: ไคลเอนต์ร้องขอข้อมูลจากเซิร์ฟเวอร์เหมือนกับการโพลแบบดั้งเดิม อย่างไรก็ตาม หากเซิร์ฟเวอร์ไม่มีข้อมูลที่สามารถส่งคืนไปยังไคลเอนต์ได้ทันที เซิร์ฟเวอร์จะไม่ส่งคืนผลลัพธ์ที่ว่างเปล่าทันที แต่จะเก็บคำขอไว้รอให้ข้อมูลมาถึง (หรือการหมดเวลาที่เหมาะสม: น้อยกว่าการหมดเวลา ajax) แล้วส่งคืนข้อมูลตามผลลัพธ์
กลไกการโพลแบบยาวแสดงในรูปด้านล่าง:
2. แนวคิดพื้นฐานของ WebsocketWebSocket เป็นโปรโตคอลสำหรับการสื่อสารฟูลดูเพล็กซ์บนการเชื่อมต่อ TCP เดียวที่ HTML5 เริ่มให้บริการ
WebSocket ทำให้การแลกเปลี่ยนข้อมูลระหว่างไคลเอนต์และเซิร์ฟเวอร์ง่ายขึ้น ช่วยให้เซิร์ฟเวอร์ส่งข้อมูลไปยังไคลเอนต์ได้อย่างแข็งขัน ใน WebSocket API เบราว์เซอร์และเซิร์ฟเวอร์จำเป็นต้องจับมือกันเท่านั้น และสามารถสร้างการเชื่อมต่อแบบถาวรระหว่างทั้งสองได้โดยตรงสำหรับการส่งข้อมูลแบบสองทิศทาง
ใน WebSocket API เบราว์เซอร์และเซิร์ฟเวอร์จำเป็นต้องดำเนินการจับมือเท่านั้น จากนั้นช่องทางที่รวดเร็วจะถูกสร้างขึ้นระหว่างเบราว์เซอร์และเซิร์ฟเวอร์ ข้อมูลสามารถส่งโดยตรงระหว่างคนทั้งสอง
ในปัจจุบัน เพื่อนำเทคโนโลยีพุชไปใช้ เว็บไซต์จำนวนมากจึงใช้ Ajax polling การโพลคือเมื่อเบราว์เซอร์ส่งคำขอ HTTP ไปยังเซิร์ฟเวอร์ในช่วงเวลาที่กำหนด (เช่นทุกๆ 1 วินาที) จากนั้นเซิร์ฟเวอร์จะส่งคืนข้อมูลล่าสุดไปยังเบราว์เซอร์ของไคลเอนต์ โมเดลดั้งเดิมนี้ทำให้เกิดข้อบกพร่องที่ชัดเจน กล่าวคือ เบราว์เซอร์จำเป็นต้องส่งคำขอไปยังเซิร์ฟเวอร์อย่างต่อเนื่อง อย่างไรก็ตาม คำขอ HTTP อาจมีส่วนหัวที่ยาว ซึ่งข้อมูลที่ถูกต้องจริงอาจเป็นเพียงส่วนเล็กๆ เท่านั้น ซึ่งเห็นได้ชัดว่าเป็นการสิ้นเปลือง . แบนด์วิธและทรัพยากรอื่น ๆ มากมาย
โปรโตคอล WebSocket ที่กำหนดโดย HTML5 สามารถประหยัดทรัพยากรเซิร์ฟเวอร์และแบนด์วิธได้ดีขึ้น และช่วยให้สามารถสื่อสารแบบเรียลไทม์ได้มากขึ้น
เบราว์เซอร์ส่งคำขอไปยังเซิร์ฟเวอร์เพื่อสร้างการเชื่อมต่อ WebSocket ผ่าน JavaScript หลังจากสร้างการเชื่อมต่อแล้ว ไคลเอนต์และเซิร์ฟเวอร์สามารถแลกเปลี่ยนข้อมูลโดยตรงผ่านการเชื่อมต่อ TCP
หลังจากที่คุณได้รับการเชื่อมต่อเว็บซ็อกเก็ตแล้ว คุณสามารถส่งข้อมูลไปยังเซิร์ฟเวอร์ผ่านเมธอด send() และรับข้อมูลที่ส่งคืนโดยเซิร์ฟเวอร์ผ่านเหตุการณ์ onmessage
3. หลักการจับมือ Websocket:หลักการจับมือของ Websocket สามารถแบ่งคร่าวๆ ได้เป็นขั้นตอนต่อไปนี้:
การใช้รหัส:
นำเข้าซ็อกเก็ต 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) สำหรับ i ใน header_str.split(/r/n): ถ้า 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=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:9527การเชื่อมต่อ: UpgradePragma: no-cacheCache-Control: no-cacheUser-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.3...อัปเกรด: 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'# ดำเนินการบิตบนไบต์ที่สอง ซึ่งก็คือ /x87 บิต 9-16 และ 127 payload = hashstr[1] & 127# เมื่อผลการดำเนินการบิตเท่ากับ 127 ไบต์ 3-10 จะเป็นความยาวของข้อมูล# ไบต์ 11-14 เป็นสตริงที่จำเป็นสำหรับการถอดรหัสมาสก์ #ข้อมูลมาจากไบต์ที่ 15 ถึงจุดสิ้นสุดหาก payload == 127: extend_payload_len = hashstr[2:10] mask = hashstr[10:14] deded = hashstr[14:]# เมื่อผลการดำเนินการบิตเท่ากับ 126 ดังนั้น 3-4 ไบต์คือความยาวของข้อมูล # 5-8 ไบต์เป็นสตริงที่จำเป็นสำหรับการถอดรหัสมาสก์ # จากนั้น data จากไบต์ที่ 9 ถึงจุดสิ้นสุดถ้า payload == 126: extend_payload_len = hashstr[2:4] mask = hashstr[4:8] deded = hashstr[8:]# เมื่อผลการดำเนินการบิตน้อยกว่าหรือเท่ากับ 125 ดังนั้นตัวเลขนี้คือความยาวของข้อมูล # 3-6 ไบต์เป็นสตริงที่จำเป็นสำหรับการถอดรหัสมาสก์ # ข้อมูลมาจากไบต์ที่ 7 ถึงจุดสิ้นสุดหาก payload <= 125: extend_payload_len = ไม่มีหน้ากาก = hashstr[2:6] ถอดรหัส = hashstr[6:]str_byte = bytearray()สำหรับฉันในช่วง (len(ถอดรหัส)): ไบต์ = ถอดรหัส[i] ^ หน้ากาก[i % 4] str_byte.append(ไบต์)พิมพ์(str_byte.decode(utf8))
วิธีการเข้ารหัส:
นำเข้า structmsg_bytes = 5555555.encode(utf8)token = b/x81length = len(msg_bytes)if length < 126: token += struct.pack(B, length)elif length == 126: token += struct.pack(!BH , 126, length)else: โทเค็น += struct.pack(!BQ, 127, length)msg = โทเค็น + msg_bytesprint(msg)4. ตัวอย่างการสื่อสารไคลเอ็นต์และเซิร์ฟเวอร์ลิงก์ตามเฟรมเวิร์ก flask และโปรโตคอล Websocket:
pip3 install gevent-websocket
จากการนำเข้าขวด Flask, คำขอจาก geventwebsocket.websocket นำเข้า WebSocket จาก gevent.pywsgi นำเข้า WSGIServerfrom geventwebsocket.handler นำเข้า WebSocketHandlerapp = Flask(__name__)@app.route(/ws)def websocket(): # รับลิงก์ของผู้ใช้ user_socket = request.environ รับ (wsgi.websocket) # type:WebSocket print (เข้าใช้สำเร็จ) ในขณะที่ True: msg = user_socket.receive() #Receive message print(msg) user_socket.send(msg) #Send message if __name__ == '__main__': # ระบุที่อยู่และหมายเลขพอร์ตไปที่ เปิดบริการ Websocket http_serv = WSGIServer ((127.0.0.1, 8001) แอป handler_class=WebSocketHandler) # เริ่มบริการ Websocket 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=Input message 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. ใช้งานการส่งข้อความโต้ตอบแบบทันที (IM) บน Websocket:รหัสเซิร์ฟเวอร์:
จากการนำเข้าขวด Flask คำขอจาก geventwebsocket.websocket นำเข้า WebSocket จาก gevent.pywsgi นำเข้า WSGIServer จาก geventwebsocket.handler นำเข้า WebSocketHandler จาก geventwebsocket.Exceptions นำเข้า WebSocketErrorimport jsonapp = Flask(__name__)user_socket_dict = {}@app.route(/ws/<username>)def websocket (ชื่อผู้ใช้): # รับลิงก์ของผู้ใช้ user_socket = request.environ.get (wsgi.websocket) # type: WebSocket user_socket_dict [ชื่อผู้ใช้] = user_socket print (ชื่อผู้ใช้ + ลิงก์สำเร็จ!) ในขณะที่ True: msg = user_socket.receive () # ยอมรับข้อความสำหรับซ็อกเก็ตใน user_socket_dict.values(): # type:WebSocket if user_socket != socket:# หากคุณส่งข้อความด้วยตัวเอง เซิร์ฟเวอร์จะไม่ตอบกลับคุณ ลอง: socket.send(json.dumps({sender: username, msg: msg})) ยกเว้น: Continueif __name__ == '__main__ ': # ระบุที่อยู่และหมายเลขพอร์ตเพื่อเปิดบริการ Websocket http_serv = WSGIServer((127.0.0.1, 8001), แอป, handler_class=WebSocketHandler) # เริ่มบริการ Websocket http_serv.serve_forever()
รหัสเอชทีเอ็ม:
<!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 solid; width: 500px; height: 800px; id=text_div></div><script> var ws = null; var ชื่อผู้ใช้ = null; WebSocket(ws://127.0.0.1:8001/ws + / + ชื่อผู้ใช้); ws.onmessage = function (ข้อมูล) { var text_div = document.getElementById(text_div); var obj_data = JSON.parse(data.data); var add_msg = <p> + obj_data.msg + : + 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 + : + ชื่อผู้ใช้ + </p>; = add_msg; ws.send(to_msg); </script></body></html>
ลูกค้า01.png
ลูกค้า02.png
ฝั่งเซิร์ฟเวอร์.png
โค้ดนี้เป็นโค้ดสาธิตซึ่งมีข้อบกพร่องและข้อบกพร่องต่างๆ เป็นหลัก ส่วนใหญ่จะใช้สำหรับการเรียนรู้ในขณะนี้ ดังนั้นจึงไม่อนุญาตให้ใช้ nitpicking ผู้ที่สนใจสามารถเพิ่มประสิทธิภาพเพิ่มเติมได้! - -
ข้างต้นคือเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการศึกษาของทุกคน ฉันหวังว่าทุกคนจะสนับสนุน VeVb Wulin Network