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;即单机、双机热备、…