websocket建立連接過程

1. 客戶端發送一個GET的http請求,請求頭要包含

connection: upgrade

host:localhost:8000。表明地址

upgrade: websocket。指明升級的協議

sec-websocket-key 。 安全驗證密鑰

sec-websocket-version。 協議版本

sec-websocket-accept 。對傳過來的key進行加密后(sha-1)后base64編碼,用于告訴客戶端自身合法。

響應狀態碼為101,標識切換協議。

在 WebSocket 連接建立的 “HTTP 握手階段”,客戶端請求頭和服務器響應頭包含多個專用字段,這些字段是實現 “協議升級” 和 “連接安全驗證” 的核心。以下逐一拆解各字段的作用,結合之前的握手示例(請求 + 響應)展開說明:

一、客戶端請求頭(發起協議升級)

客戶端發送的是?HTTP GET 請求,但通過特殊頭字段告知服務器:“我希望將當前 HTTP 連接升級為 WebSocket 連接”。核心字段及作用如下:

字段名示例值核心作用細節說明
GET(請求方法)GET / HTTP/1.1觸發協議升級的基礎請求WebSocket 握手僅支持?GET?方法,因為無需向服務器提交數據,僅需 “發起連接請求”。路徑(/)通常與 WebSocket 服務綁定的路徑一致(如?ws://localhost:8080/chat?對應?GET /chat)。
Hostlocalhost:8080指定目標服務器地址與普通 HTTP 請求一致,用于服務器在多域名部署時識別 “客戶端要連接哪個服務”(如反向代理場景)。
ConnectionUpgrade聲明 “要升級連接類型”必須設置為?Upgrade,告知服務器:“這不是普通 HTTP 請求,而是要升級連接協議的請求”。
Upgradewebsocket聲明 “要升級到的協議”核心字段,明確指定升級目標是?websocket?協議(區分于其他可能的升級協議,如?HTTP/2)。
Sec-WebSocket-KeydGhlIHNhbXBsZSBub25jZQ==安全驗證的隨機密鑰1. 客戶端生成的?16 字節隨機字符串(經 Base64 編碼后的值);
2. 服務器需將該值與固定 GUID(258EAFA5-E914-47DA-95CA-C5AB0DC85B11)拼接,再通過 SHA-1 哈希、Base64 編碼,生成?Sec-WebSocket-Accept?響應值;
3. 作用:防止 “偽 WebSocket 請求”(如普通 HTTP 請求偽造升級意圖),確保服務器確實理解 WebSocket 協議。
Sec-WebSocket-Version13聲明支持的 WebSocket 版本必須設置為?13(當前主流標準版本),低版本(如 8、10)已被淘汰。服務器若不支持該版本,會返回?426 Upgrade Required?錯誤,并在?Sec-WebSocket-Version?頭中告知支持的版本。
Origin(可選)http://localhost:8080聲明請求來源僅在瀏覽器環境下發送(如前端頁面跨域連接時),服務器可通過該字段做 “跨域權限校驗”(如允許?http://example.com?來源的連接)。
Sec-WebSocket-Protocol(可選)chat, game聲明支持的子協議客戶端告知服務器 “希望使用的 WebSocket 子協議”(如自定義的?chat?協議用于聊天,game?協議用于游戲)。服務器若支持,會在響應頭中返回匹配的子協議;若不支持,會忽略該字段。
Sec-WebSocket-Extensions(可選)permessage-deflate聲明支持的擴展客戶端告知服務器 “希望啟用的 WebSocket 擴展”(如?permessage-deflate?用于數據壓縮),減少傳輸體積。服務器需在響應中確認啟用的擴展。

二、服務器響應頭(同意協議升級)

服務器驗證請求頭合法后,返回?101 Switching Protocols?響應,標志 “HTTP 連接正式升級為 WebSocket 連接”。核心字段及作用如下:

字段名示例值核心作用細節說明
HTTP/1.1 101 Switching Protocols(狀態碼)-確認協議升級唯一合法的狀態碼,含義是 “服務器同意客戶端的協議升級請求”。若返回其他狀態碼(如 400、426),表示握手失敗。
ConnectionUpgrade呼應客戶端的升級聲明必須與客戶端的?Connection: Upgrade?一致,確認 “連接類型將升級”。
Upgradewebsocket確認升級到 WebSocket 協議與客戶端的?Upgrade: websocket?一致,明確告知客戶端 “協議升級目標已確認”。
Sec-WebSocket-Accepts3pPLMBiTxaQ9kYGzzhZRbK+xOo=驗證通過的憑證1. 由客戶端的?Sec-WebSocket-Key?計算而來(規則:Key + GUID?→ SHA-1 哈希 → Base64 編碼);
2. 客戶端收到后會反向驗證:若計算結果與該值一致,說明服務器確實理解 WebSocket 協議,握手成功;否則握手失敗,關閉連接;
3. 作用:防止 “中間人攻擊” 或 “錯誤連接”,確保通信雙方均支持 WebSocket。
Sec-WebSocket-Protocol(可選)chat確認啟用的子協議若客戶端發送了?Sec-WebSocket-Protocol,服務器需從中選擇一個支持的子協議返回(如選?chat);若不支持任何子協議,可忽略該字段(客戶端需處理 “無可用子協議” 的情況)。
Sec-WebSocket-Extensions(可選)permessage-deflate確認啟用的擴展若客戶端發送了?Sec-WebSocket-Extensions,服務器需返回 “同意啟用的擴展”(如?permessage-deflate);若不支持,可忽略該字段(客戶端不啟用擴展)。
Access-Control-Allow-Origin(可選)http://localhost:8080跨域權限控制僅在跨域場景下需要(如前端頁面部署在?http://a.com,連接?ws://b.com)。服務器通過該字段允許指定來源的客戶端連接,避免跨域限制。

三、關鍵字段的 “協同邏輯”(為什么需要這些字段?)

WebSocket 握手的核心是 “安全地完成協議升級”,避免與普通 HTTP 請求混淆,以下是關鍵字段的協同邏輯:

  1. Connection: Upgrade?+?Upgrade: websocket
    這對字段是 “升級信號”,明確告知服務器 “這不是普通 HTTP 請求”,而是要切換到 WebSocket 協議。
  2. Sec-WebSocket-Key?+?Sec-WebSocket-Accept
    這對字段是 “安全驗證”,確保服務器確實支持 WebSocket(而非普通 HTTP 服務器誤響應),同時防止客戶端連接到錯誤的服務。
  3. Sec-WebSocket-Version
    確保客戶端與服務器使用相同的 WebSocket 標準版本,避免版本不兼容導致通信失敗。

四、握手失敗的常見場景(字段錯誤導致)

若請求 / 響應頭字段不合法,握手會立即失敗,連接關閉。常見場景包括:

  • 客戶端未發送?Upgrade: websocket?或?Connection: Upgrade?→ 服務器視為普通 HTTP 請求,返回 400 錯誤;
  • 客戶端?Sec-WebSocket-Version?不是 13 → 服務器返回 426 錯誤,并告知支持的版本;
  • 服務器計算的?Sec-WebSocket-Accept?與客戶端預期不符 → 客戶端判定服務器不支持 WebSocket,關閉連接;
  • 跨域場景下服務器未返回?Access-Control-Allow-Origin?→ 瀏覽器因跨域限制,拒絕建立連接。

通過以上字段的協作,WebSocket 才能安全、可靠地完成從 HTTP 到 WebSocket 協議的升級,為后續的雙向實時通信奠定基礎。

代碼demo

服務端

const WebSocket = require('ws');
const http = require('http');
const fs = require('fs');// 創建 HTTP 服務器提供客戶端頁面
const server = http.createServer((req, res) => {if (req.url === '/') {res.writeHead(200, { 'Content-Type': 'text/html' });res.end(fs.readFileSync('client.html'));} else {res.writeHead(404);res.end();}
});// 創建 WebSocket 服務器,附加到 HTTP 服務器
const wss = new WebSocket.Server({ server });// 監聽連接事件
wss.on('connection', (ws) => {console.log('客戶端已連接');// 向客戶端發送歡迎消息ws.send(JSON.stringify({ type: 'system', message: '歡迎連接到 WebSocket 服務器!' }));// 監聽客戶端消息ws.on('message', (data) => {console.log(`收到客戶端消息: ${data}`);// 解析客戶端消息try {const message = JSON.parse(data);// 回復客戶端ws.send(JSON.stringify({type: 'reply',message: `服務器已收到: ${message.content}`,timestamp: new Date().toISOString()}));} catch (e) {ws.send(JSON.stringify({type: 'error',message: '無效的消息格式'}));}});// 監聽連接關閉ws.on('close', () => {console.log('客戶端已斷開連接');});// 監聽錯誤ws.on('error', (error) => {console.error('WebSocket 錯誤:', error);});
});// 啟動服務器
const PORT = 8080;
server.listen(PORT, () => {console.log(`服務器運行在 http://localhost:${PORT}`);console.log(`WebSocket 服務已啟動,等待連接...`);
});

客戶端:

<!DOCTYPE html>
<html><head><title>WebSocket 示例</title><style>.container {max-width: 800px;margin: 0 auto;padding: 20px;}#messages {border: 1px solid #ccc;height: 400px;overflow-y: auto;margin-bottom: 20px;padding: 10px;}.message {margin: 5px 0;padding: 8px;border-radius: 4px;}.system {background-color: #f0f0f0;}.incoming {background-color: #e1f5fe;}.outgoing {background-color: #e8f5e9;text-align: right;}.error {background-color: #ffebee;}#inputArea {display: flex;gap: 10px;}#messageInput {flex-grow: 1;padding: 8px;}button {padding: 8px 16px;}</style>
</head><body><div class="container"><h1>WebSocket 通信示例</h1><div id="connectionStatus">未連接</div><div id="messages"></div><div id="inputArea"><input type="text" id="messageInput" placeholder="輸入消息..."><button onclick="connectWebSocket()">連接</button><button onclick="disconnectWebSocket()">斷開</button><button onclick="sendMessage()">發送</button></div></div><script>let ws;const messagesDiv = document.getElementById('messages');const statusDiv = document.getElementById('connectionStatus');const messageInput = document.getElementById('messageInput');// 連接 WebSocket 服務器function connectWebSocket() {// 關閉已有的連接if (ws) {ws.close();}// 創建 WebSocket 連接// 注意:ws:// 對應 HTTP,wss:// 對應 HTTPSws = new WebSocket('ws://localhost:8080');// 連接建立事件ws.onopen = () => {console.log('WebSocket 連接已建立');statusDiv.textContent = '已連接';statusDiv.style.color = 'green';addMessage('系統消息:連接已建立', 'system');};// 接收消息事件ws.onmessage = (event) => {console.log('收到消息:', event.data);const message = JSON.parse(event.data);addMessage(message.message, message.type === 'error' ? 'error' : 'incoming');};// 連接關閉事件ws.onclose = (event) => {console.log(`WebSocket 連接已關閉,代碼: ${event.code}, 原因: ${event.reason}`);statusDiv.textContent = '已斷開';statusDiv.style.color = 'red';addMessage(`系統消息:連接已關閉 (${event.code})`, 'system');ws = null;};// 錯誤事件ws.onerror = (error) => {console.error('WebSocket 錯誤:', error);addMessage(`錯誤:${error.message}`, 'error');};}// 斷開連接function disconnectWebSocket() {if (ws) {ws.close(1000, '客戶端主動斷開');}}// 發送消息function sendMessage() {if (!ws || ws.readyState !== WebSocket.OPEN) {alert('請先建立連接');return;}const message = messageInput.value.trim();if (!message) return;// 發送消息到服務器ws.send(JSON.stringify({content: message,timestamp: new Date().toISOString()}));// 在本地顯示發送的消息addMessage(`我: ${message}`, 'outgoing');messageInput.value = '';}// 添加消息到界面function addMessage(text, type) {const messageDiv = document.createElement('div');messageDiv.className = `message ${type}`;messageDiv.textContent = text;messagesDiv.appendChild(messageDiv);// 滾動到底部messagesDiv.scrollTop = messagesDiv.scrollHeight;}// 監聽回車鍵發送消息messageInput.addEventListener('keypress', (e) => {if (e.key === 'Enter') {sendMessage();}});</script>
</body></html>

請求頭:

GET ws://localhost:8080/ HTTP/1.1
Host: localhost:8080
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Upgrade: websocket
Origin: http://localhost:8080
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9
Sec-WebSocket-Key: hoR5iuo6igGV8GdVrg6/hw==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

響應頭

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: sYx+sebTITkvLKI5+SW8icTNWkc=

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/920855.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/920855.shtml
英文地址,請注明出處:http://en.pswp.cn/news/920855.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Simulink庫文件-一種低通濾波模塊搭建方法

在汽車電控系統應用層開發中&#xff0c;經常會用到低通濾波模塊&#xff0c;其主要作用是去除輸入信號中的高頻干擾&#xff0c;防止由于輸入信號的干擾引起后續執行系統的非預期頻繁波動。本文介紹簡要介紹低通濾波的定義及作用&#xff0c;并介紹一種低通濾波模塊simulink搭…

【C++游記】AVL樹

楓の個人主頁 你不能改變過去&#xff0c;但你可以改變未來 算法/C/數據結構/C Hello&#xff0c;這里是小楓。C語言與數據結構和算法初階兩個板塊都更新完畢&#xff0c;我們繼續來學習C的內容呀。C是接近底層有比較經典的語言&#xff0c;因此學習起來注定枯燥無味&#xf…

音視頻學習(六十二):H264中的SEI

什么是SEI? 在 H.264 視頻編碼標準中&#xff0c;補充增強信息&#xff08;Supplemental Enhancement Information&#xff0c;SEI&#xff09; 是一種特殊的 NAL&#xff08;網絡抽象層&#xff09;單元。它不像序列參數集&#xff08;SPS&#xff09;或圖像參數集&#xff0…

docker run 后報錯/bin/bash: /bin/bash: cannot execute binary file總結

以下方法來源于AI&#xff0c;個人僅驗證了第三條便成功執行 1. 鏡像與宿主機架構不匹配 比如&#xff1a; 你是 x86_64 的機器&#xff0c;但鏡像是 ARM64 的&#xff08;或反之&#xff09;。在 PC 上拉了樹莓派用的鏡像。查看鏡像架構 docker inspect <image_name> | …

【Redisson 加鎖源碼解析】

Redisson 源碼解析 —— 分布式鎖實現過程 在分布式系統中&#xff0c;分布式鎖 是非常常見的需求&#xff0c;用來保證多個節點之間的互斥操作。Redisson 是 Redis 的一個 Java 客戶端&#xff0c;它提供了對分布式鎖的良好封裝。本文將從源碼角度剖析 Redisson 的分布式鎖實現…

uni-app支持單多選、搜索、查詢、限制能否點擊組件

<template><view class="multi-select-container" :class="{ single-select: !multiple, no-search: !searchable }"><!-- 當組件被禁用時,直接顯示選中的內容 --><view class="disabled-display" v-if="disabled &a…

TFT屏幕:STM32硬件SPI+DMA+隊列自動傳輸

看了網上的很多的SPIDMA的代碼&#xff0c;感覺都有一些缺陷&#xff0c;就是基本都是需要有手動等待DMA完成的這個操作&#xff0c;我感覺這種等待操作在很大程度上浪費了時間&#xff0c;那么我加入的“隊列”就是一種將等待時間利用起來的方法。原本的SPIDMA的操作邏輯如下圖…

AI操作系統語言模型設計 之1 基于意識的Face-Gate-Window的共軛路徑的思維-認知-情感嵌套模型

摘要&#xff08;AI生成&#xff09;本文提出了一種創新的AI操作系統語言模型設計框架&#xff0c;將人類意識活動的分層結構映射到人工智能系統中。該模型包含三個嵌套層次&#xff1a;理性思維層&#xff08;Face層&#xff09;&#xff1a;采用雙面膠隱喻&#xff08;A/B面&…

瘋狂星期四文案網第57天運營日記

網站運營第57天&#xff0c;點擊觀站&#xff1a; 瘋狂星期四 crazy-thursday.com 全網最全的瘋狂星期四文案網站 運營報告 今日訪問量 今日搜索引擎收錄情況

SQLark:一款面向信創應用開發者的數據庫開發和管理工具

SQLark 是一款面向信創應用開發者的數據庫開發和管理工具&#xff0c;用于快速查詢、創建和管理不同類型的數據庫系統&#xff0c;現已支持達夢、Oracle、MySQL、PostgreSQL 數據庫。 SQLark 提供了對多種數據庫的連接支持&#xff0c;實現跨平臺數據庫管理的無縫切換&#xff…

BigDecimal——解決Java浮點數值精度問題:快速入門與使用

在Java開發中&#xff0c;涉及金額計算、科學計數或需要高精度數值處理時&#xff0c;你是否遇到過這樣的困惑&#xff1f;用double計算0.1加0.2&#xff0c;結果竟不是0.3&#xff1b;用float存儲商品價格&#xff0c;小數點后兩位莫名多出幾位亂碼&#xff1b;甚至在金融系統…

wpf之WrapPanel

前言 WrapPanel類似winform中的FlowLayoutPanel&#xff0c;采用流式布局。 1、Orientation 該屬性指定WrapPanel中子空間布局的方向&#xff0c;有水平和垂直方向兩種 1&#xff09;Horizontal 水平方向 子元素Button按照水平方向排列&#xff0c;如果一行排滿了自動換下一…

Woody:開源Java應用性能診斷分析工具

核心價值 Woody是一款專注于Java應用性能問題診斷的工具&#xff0c;旨在幫助開發者 定位高GC頻率問題&#xff0c;識別內存分配熱點分析CPU使用率過高的代碼路徑追蹤接口耗時瓶頸&#xff0c;定位內部操作耗時占比診斷鎖競爭問題&#xff0c;支持精準優化針對特定業務接口/請…

《山東棒球》板球比賽規則·棒球1號位

? Baseball vs Cricket 終極科普&#xff5c;規則異同發展史全解&#xff01;Hey sports babes&#xff01;別再傻傻分不清棒球?和板球&#xff01;全網最清晰雙運動對照指南來啦&#xff5e;? 棒球 Baseball&#xff5c;美式激情風暴Core Goal核心目標擊球員&#xff08;Ba…

【游戲開發】Houdini相較于Blender在游戲開發上有什么優劣勢?我該怎么選擇開發工具?

在游戲開發中&#xff0c;Houdini與Blender的選擇需結合項目規模、技術需求和團隊資源綜合考量。以下是兩者的核心優劣勢對比及決策建議&#xff1a; 一、核心優劣勢對比 Houdini的優勢與局限 優勢&#xff1a;程序化內容生成的統治力 Houdini的節點系統&#xff08;如VEX語言、…

基于開源AI智能名片鏈動2+1模式S2B2C商城小程序的用戶活躍度提升與價值挖掘策略研究

摘要&#xff1a;本文聚焦于在開源AI智能名片鏈動21模式S2B2C商城小程序環境下&#xff0c;探討如何提高用戶活躍度并挖掘用戶價值。在用戶留存的基礎上&#xff0c;通過分析該特定模式與小程序的特點&#xff0c;提出一系列針對性的策略&#xff0c;旨在借助開源AI智能名片以及…

《投資-41》- 自然=》生物=》人類社會=》商業=》金融=》股市=》投資,其層層疊加構建中內在的相似的規律和規則

從自然到投資的層層遞進中&#xff0c;盡管各領域看似差異巨大&#xff0c;但內在遵循著相似的規律和規則。這些規律體現了“底層邏輯的普適性”&#xff0c;即不同系統在動態平衡、資源分配、信息傳遞和反饋調節等方面具有共性。以下是關鍵規律的解析&#xff1a;1. 能量流動與…

VSCode中調試python腳本

VSCode中安裝以下插件 ms-python.python&#xff1a;python調試ms-python.vscode-pylance&#xff1a;代碼跳轉&#xff08;非必要&#xff09; 配置launch.json 在當前工作區&#xff0c;按此路徑.vscode\launch.json新建launch.json文件&#xff0c;并配置以下參數&#x…

動作指令活體檢測通過動態交互驗證真實活人,保障安全

在當今社會&#xff0c;人臉識別技術已深入日常生活的方方面面&#xff0c;從手機解鎖、移動支付到遠程開戶、門禁考勤&#xff0c;人臉識別技術已無處不在。然而&#xff0c;這項技術也面臨著嚴峻的安全挑戰&#xff1a;打印照片、播放視頻、制作3D面具等簡單的“欺騙手段”都…

KingbaseES數據庫:開發基礎教程,從部署到安全的全方位實踐

KingbaseES數據庫&#xff1a;開發基礎教程&#xff0c;從部署到安全的全方位實踐 KingbaseES數據庫&#xff1a;開發基礎教程&#xff0c;從部署到安全的全方位實踐&#xff0c;本文圍繞 KingbaseES 數據庫開發核心基礎展開。先介紹三種部署模式&#xff0c;即單機、雙機熱備、…