如何理解WebSocket握手操作及实现JavaScript端WebSocket连接?
Great question about WebSocket handshake mechanics—let’s break this down clearly, since it’s the critical first step that bridges HTTP to full-duplex WebSocket communication.
Why the Handshake Exists
First off, the handshake is how a regular HTTP connection gets "upgraded" to a WebSocket connection. Browsers and servers use this process to mutually agree to switch protocols, since WebSockets operate on a totally different model (persistent, full-duplex communication) compared to HTTP's stateless request-response cycle.
Step-by-Step Handshake Flow
Let’s dive deeper into the exact back-and-forth you mentioned:
1. Client Initiates the Upgrade Request
When you run const socket = new WebSocket('ws://localhost:8080');, the browser sends an HTTP GET request with specific headers that signal it wants to switch to WebSocket. Here’s a simplified version of that request:
GET / HTTP/1.1 Host: localhost:8080 Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13
Upgrade: websocket&Connection: Upgrade: These mandatory headers explicitly tell the server, "I want to switch our communication protocol to WebSocket."Sec-WebSocket-Key: A random base64-encoded string generated by the client. This isn’t a security token—it’s a safeguard to prevent accidental or malicious protocol upgrades, ensuring the server actually understands WebSocket rather than just responding to random headers.Sec-WebSocket-Version: 13: Specifies the standard WebSocket protocol version the client uses. Servers will reject requests using older, outdated versions.
2. Server Validates & Responds
The server must validate the client’s request before agreeing to the upgrade. Here’s what a valid response looks like:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
101 Switching Protocols: This status code confirms the server is successfully switching from HTTP to the WebSocket protocol.Sec-WebSocket-Accept: This is the critical validation step, calculated as follows:- Take the client’s
Sec-WebSocket-Keyvalue. - Append the fixed magic string
258EAFA5-E914-47DA-95CA-C5AB0DC85B11(defined in the official WebSocket spec). - Compute the SHA-1 hash of the combined string.
- Encode that hash as base64.
For example, combining the client keydGhlIHNhbXBsZSBub25jZQ==with the magic string producesdGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11. The SHA-1 hash of this string base64-encodes to exactly theSec-WebSocket-Acceptvalue in your example. This calculation proves the server properly processed the client’s key, so the client knows it’s connecting to a legitimate WebSocket server.
- Take the client’s
3. Connection Established
Once the client receives the valid 101 response with the correct Sec-WebSocket-Accept value, the HTTP connection is replaced by a persistent WebSocket connection. Now both sides can send data back and forth at any time (full-duplex), using the lightweight WebSocket frame format instead of HTTP requests/responses.
Key Implementation Details to Keep in Mind
- Mandatory Headers: Both client and server must include the required headers (
Upgrade,Connection,Sec-WebSocket-Keyfor clients,Sec-WebSocket-Acceptfor servers). Missing or incorrect headers will cause the handshake to fail. - CORS Rules: WebSocket connections follow the same CORS policies as HTTP requests. If your client is on a different origin than the server, the server must handle
Originheaders appropriately to allow the connection. - Proxy Pitfalls: Some proxy servers block WebSocket upgrades or require additional configuration (like allowing the
Upgradeheader). This is a common gotcha when deploying WebSocket apps to production. - Error Handling: If the server rejects the upgrade, it will send an HTTP error code (e.g., 400 Bad Request) instead of a 101 response, triggering the client’s
onerrorevent.
Example Server-Side Calculation (Node.js)
To make this concrete, here’s how you’d compute the Sec-WebSocket-Accept value in Node.js:
const crypto = require('crypto'); function computeAcceptKey(clientKey) { const magicString = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; return crypto.createHash('sha1') .update(clientKey + magicString) .digest('base64'); } // Usage example: const clientKey = 'dGhlIHNhbXBsZSBub25jZQ=='; console.log(computeAcceptKey(clientKey)); // Outputs: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
内容的提问来源于stack exchange,提问作者avi dahan

