日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

WebSocket介绍

發布時間:2023/12/20 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 WebSocket介绍 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

WebSocket協議是基于TCP的一種新的協議。WebSocket最初在HTML5規范中被引用為TCP連接,作為基于TCP的套接字API的占位符。它實現了瀏覽器與服務器全雙工(full-duplex)通信。其本質是保持TCP連接,在瀏覽器和服務端通過Socket進行通信。

?本文將使用Python編寫Socket服務端,一步一步分析請求過程!!!

1. 啟動服務端

import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('127.0.0.1', 8002)) sock.listen(5) # 等待用戶連接 conn, address = sock.accept() ... ... ...

啟動Socket服務器后,等待用戶【連接】,然后進行收發數據。

2. 客戶端連接

<script type="text/javascript">var socket = new WebSocket("ws://127.0.0.1:8002/xxoo");... </script>

當客戶端向服務端發送連接請求時,不僅連接還會發送【握手】信息,并等待服務端響應,至此連接才創建成功!

3. 建立連接【握手】

import socketsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('127.0.0.1', 8002)) sock.listen(5) # 獲取客戶端socket對象 conn, address = sock.accept() # 獲取客戶端的【握手】信息 data = conn.recv(1024) ... ... ... conn.send('響應【握手】信息')

請求和響應的【握手】信息需要遵循規則:

  • 從請求【握手】信息中提取?Sec-WebSocket-Key
  • 利用magic_string 和 Sec-WebSocket-Key 進行hmac1加密,再進行base64加密
  • 將加密結果響應給客戶端

注:magic string為:258EAFA5-E914-47DA-95CA-C5AB0DC85B11

請求【握手】信息為:

GET /chatsocket HTTP/1.1 Host: 127.0.0.1:8002 Connection: Upgrade Pragma: no-cache Cache-Control: no-cache Upgrade: websocket Origin: http://localhost:63342 Sec-WebSocket-Version: 13 Sec-WebSocket-Key: mnwFxiOlctXFN/DeMt1Amg== Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits ... ...

提取Sec-WebSocket-Key值并加密:

import socket import base64 import hashlibdef get_headers(data):"""將請求頭格式化成字典:param data::return:"""header_dict = {}data = str(data, encoding='utf-8')for i in data.split('\r\n'):print(i)header, body = data.split('\r\n\r\n', 1)header_list = header.split('\r\n')for i in range(0, len(header_list)):if i == 0:if len(header_list[i].split(' ')) == 3:header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')else:k, v = header_list[i].split(':', 1)header_dict[k] = v.strip()return header_dictsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('127.0.0.1', 8002)) sock.listen(5)conn, address = sock.accept() data = conn.recv(1024) headers = get_headers(data) # 提取請求頭信息 # 對請求頭中的sec-websocket-key進行加密 response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \"Upgrade:websocket\r\n" \"Connection: Upgrade\r\n" \"Sec-WebSocket-Accept: %s\r\n" \"WebSocket-Location: ws://%s%s\r\n\r\n" magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' value = headers['Sec-WebSocket-Key'] + magic_string ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest()) response_str = response_tpl % (ac.decode('utf-8'), headers['Host'], headers['url']) # 響應【握手】信息 conn.send(bytes(response_str, encoding='utf-8')) ... ... ...

4.客戶端和服務端收發數據

客戶端和服務端傳輸數據時,需要對數據進行【封包】和【解包】。客戶端的JavaScript類庫已經封裝【封包】和【解包】過程,但Socket服務端需要手動實現。

第一步:獲取客戶端發送的數據【解包】

info = conn.recv(8096)payload_len = info[1] & 127if payload_len == 126:extend_payload_len = info[2:4]mask = info[4:8]decoded = info[8:]elif payload_len == 127:extend_payload_len = info[2:10]mask = info[10:14]decoded = info[14:]else:extend_payload_len = Nonemask = info[2:6]decoded = info[6:]bytes_list = bytearray()for i in range(len(decoded)):chunk = decoded[i] ^ mask[i % 4]bytes_list.append(chunk)body = str(bytes_list, encoding='utf-8')print(body) 基于Python實現解包過程(未實現長內容)

解包詳細過程:?

0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-------+-+-------------+-------------------------------+|F|R|R|R| opcode|M| Payload len | Extended payload length ||I|S|S|S| (4) |A| (7) | (16/64) ||N|V|V|V| |S| | (if payload len==126/127) || |1|2|3| |K| | |+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +| Extended payload length continued, if payload len == 127 |+ - - - - - - - - - - - - - - - +-------------------------------+| |Masking-key, if MASK set to 1 |+-------------------------------+-------------------------------+| Masking-key (continued) | Payload Data |+-------------------------------- - - - - - - - - - - - - - - - +: Payload Data continued ... :+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +| Payload Data continued ... |+---------------------------------------------------------------+

The MASK bit simply tells whether the message is encoded. Messages from the client must be masked, so your server should expect this to be 1. (In fact,?section 5.1 of the spec?says that your server must disconnect from a client if that client sends an unmasked message.) When sending a frame back to the client, do not mask it and do not set the mask bit. We'll explain masking later.?Note: You have to mask messages even when using a secure socket.RSV1-3 can be ignored, they are for extensions.

The opcode field defines how to interpret the payload data:?0x0?for continuation,?0x1?for text (which is always encoded in UTF-8),?0x2?for binary, and other so-called "control codes" that will be discussed later. In this version of WebSockets,?0x3?to?0x7?and?0xB?to?0xF?have no meaning.

The FIN bit tells whether this is the last message in a series. If it's 0, then the server will keep listening for more parts of the message; otherwise, the server should consider the message delivered. More on this later.

Decoding Payload Length

To read the payload data, you must know when to stop reading. That's why the payload length is important to know. Unfortunately, this is somewhat complicated. To read it, follow these steps:

  • Read bits 9-15 (inclusive) and interpret that as an unsigned integer. If it's 125 or less, then that's the length; you're?done. If it's 126, go to step 2. If it's 127, go to step 3.
  • Read the next 16 bits and interpret those as an unsigned integer. You're?done.
  • Read the next 64 bits and interpret those as an unsigned integer (The most significant bit MUST be 0). You're?done.
  • Reading and Unmasking the Data

    If the MASK bit was set (and it should be, for client-to-server messages), read the next 4 octets (32 bits); this is the masking key.?Once the payload length and masking key is decoded, you can go ahead and read that number of bytes from the socket. Let's call the data?ENCODED, and the key?MASK. To get?DECODED, loop through the octets (bytes a.k.a. characters for text data) of?ENCODED?and XOR the octet with the (i modulo 4)th octet of MASK. In pseudo-code (that happens to be valid JavaScript):

    ?

    var DECODED = "";
    for (var i = 0; i < ENCODED.length; i++) {
    ? ? DECODED[i] = ENCODED[i] ^ MASK[i % 4];
    }

    ?

    Now you can figure out what?DECODED?means depending on your application.

    ?第二步:向客戶端發送數據【封包】

    def send_msg(conn, msg_bytes):"""WebSocket服務端向客戶端發送消息:param conn: 客戶端連接到服務器端的socket對象,即: conn,address = socket.accept():param msg_bytes: 向客戶端發送的字節:return: """import structtoken = b"\x81"length = len(msg_bytes)if length < 126:token += struct.pack("B", length)elif length <= 0xFFFF:token += struct.pack("!BH", 126, length)else:token += struct.pack("!BQ", 127, length)msg = token + msg_bytesconn.send(msg)return True View Code

    5. 基于Python實現簡單示例

    a. 基于Python socket實現的WebSocket服務端:

    #!/usr/bin/env python # -*- coding:utf-8 -*- import socket import base64 import hashlibdef get_headers(data):"""將請求頭格式化成字典:param data::return:"""header_dict = {}data = str(data, encoding='utf-8')header, body = data.split('\r\n\r\n', 1)header_list = header.split('\r\n')for i in range(0, len(header_list)):if i == 0:if len(header_list[i].split(' ')) == 3:header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')else:k, v = header_list[i].split(':', 1)header_dict[k] = v.strip()return header_dictdef send_msg(conn, msg_bytes):"""WebSocket服務端向客戶端發送消息:param conn: 客戶端連接到服務器端的socket對象,即: conn,address = socket.accept():param msg_bytes: 向客戶端發送的字節:return: """import structtoken = b"\x81"length = len(msg_bytes)if length < 126:token += struct.pack("B", length)elif length <= 0xFFFF:token += struct.pack("!BH", 126, length)else:token += struct.pack("!BQ", 127, length)msg = token + msg_bytesconn.send(msg)return Truedef run():sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)sock.bind(('127.0.0.1', 8003))sock.listen(5)conn, address = sock.accept()data = conn.recv(1024)headers = get_headers(data)response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \"Upgrade:websocket\r\n" \"Connection:Upgrade\r\n" \"Sec-WebSocket-Accept:%s\r\n" \"WebSocket-Location:ws://%s%s\r\n\r\n"value = headers['Sec-WebSocket-Key'] + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())response_str = response_tpl % (ac.decode('utf-8'), headers['Host'], headers['url'])conn.send(bytes(response_str, encoding='utf-8'))while True:try:info = conn.recv(8096)except Exception as e:info = Noneif not info:breakpayload_len = info[1] & 127if payload_len == 126:extend_payload_len = info[2:4]mask = info[4:8]decoded = info[8:]elif payload_len == 127:extend_payload_len = info[2:10]mask = info[10:14]decoded = info[14:]else:extend_payload_len = Nonemask = info[2:6]decoded = info[6:]bytes_list = bytearray()for i in range(len(decoded)):chunk = decoded[i] ^ mask[i % 4]bytes_list.append(chunk)body = str(bytes_list, encoding='utf-8')send_msg(conn,body.encode('utf-8'))sock.close()if __name__ == '__main__':run()

    b. 利用JavaScript類庫實現客戶端

    <!DOCTYPE html> <html> <head lang="en"><meta charset="UTF-8"><title></title> </head> <body><div><input type="text" id="txt"/><input type="button" id="btn" value="提交" οnclick="sendMsg();"/><input type="button" id="close" value="關閉連接" οnclick="closeConn();"/></div><div id="content"></div><script type="text/javascript">var socket = new WebSocket("ws://127.0.0.1:8003/chatsocket");socket.onopen = function () {/* 與服務器端連接成功后,自動執行 */var newTag = document.createElement('div');newTag.innerHTML = "【連接成功】";document.getElementById('content').appendChild(newTag);};socket.onmessage = function (event) {/* 服務器端向客戶端發送數據時,自動執行 */var response = event.data;var newTag = document.createElement('div');newTag.innerHTML = response;document.getElementById('content').appendChild(newTag);};socket.onclose = function (event) {/* 服務器端主動斷開連接時,自動執行 */var newTag = document.createElement('div');newTag.innerHTML = "【關閉連接】";document.getElementById('content').appendChild(newTag);};function sendMsg() {var txt = document.getElementById('txt');socket.send(txt.value);txt.value = "";}function closeConn() {socket.close();var newTag = document.createElement('div');newTag.innerHTML = "【關閉連接】";document.getElementById('content').appendChild(newTag);}</script> </body> </html>

    6. 基于Tornado框架實現Web聊天室

    Tornado是一個支持WebSocket的優秀框架,其內部原理正如1~5步驟描述,當然Tornado內部封裝功能更加完整。

    以下是基于Tornado實現的聊天室示例:

    #!/usr/bin/env python # -*- coding:utf-8 -*- import uuid import json import tornado.ioloop import tornado.web import tornado.websocketclass IndexHandler(tornado.web.RequestHandler):def get(self):self.render('index.html')class ChatHandler(tornado.websocket.WebSocketHandler):# 用戶存儲當前聊天室用戶waiters = set()# 用于存儲歷時消息messages = []def open(self):"""客戶端連接成功時,自動執行:return: """ChatHandler.waiters.add(self)uid = str(uuid.uuid4())self.write_message(uid)for msg in ChatHandler.messages:content = self.render_string('message.html', **msg)self.write_message(content)def on_message(self, message):"""客戶端連發送消息時,自動執行:param message: :return: """msg = json.loads(message)ChatHandler.messages.append(message)for client in ChatHandler.waiters:content = client.render_string('message.html', **msg)client.write_message(content)def on_close(self):"""客戶端關閉連接時,,自動執行:return: """ChatHandler.waiters.remove(self)def run():settings = {'template_path': 'templates','static_path': 'static',}application = tornado.web.Application([(r"/", IndexHandler),(r"/chat", ChatHandler),], **settings)application.listen(8888)tornado.ioloop.IOLoop.instance().start()if __name__ == "__main__":run() app.py <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Python聊天室</title> </head> <body><lt;div><input type="text" id="txt"/><input type="button" id="btn" value="提交" onclick="sendMsg();"/><input type="button" id="close" value="關閉連接" onclick="closeConn();"/></div><div id="container" style="border: 1px solid #dddddd;margin: 20px;min-height: 500px;"></div><script src="/static/jquery-2.1.4.min.js"></script><script type="text/javascript">$(function () {wsUpdater.start();});var wsUpdater = {socket: null,uid: null,start: function() {var url = "ws://127.0.0.1:8888/chat";wsUpdater.socket = new WebSocket(url);wsUpdater.socket.onmessage = function(event) {console.log(event);if(wsUpdater.uid){wsUpdater.showMessage(event.data);}else{wsUpdater.uid = event.data;}}},showMessage: function(content) {$('#container').append(content);}};function sendMsg() {var msg = {uid: wsUpdater.uid,message: $("#txt").val()};wsUpdater.socket.send(JSON.stringify(msg));}</script></body> </html> index.html

    ?

    ?

    轉載于:https://www.cnblogs.com/hedeyong/p/8128129.html

    總結

    以上是生活随笔為你收集整理的WebSocket介绍的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。