1. 什么是握手
我们知道,所有的 tcp 请求都需要经历三次握手,如下图:
握手是websockets中的web,他是http到ws的桥梁 为什么是三次,如果是两次会怎么样呢,我们假象一下,
客户端:客户端请求连接
服务端:服务端回复客户端我已经准备好了
这时握手终止,服务端就会想,客户端内小子他有没有准备好啊,心好慌。。。
直到客户端回复,大哥我已经准备好了,来吧,开始干大事
2. 我们为什么握手
web现存的漏洞类,如果我们使用HTTP轮询存在于Web应用程序中使用http 如:
》以明文形式传输敏感数据(WS://而不是WSS://)
》用户输入验证问题
》身份验证/授权问题
》Origin Header验证/跨站请求伪造(CSRF)
感觉纯粹的ajax都不是很好解决,而在WebSocket握手期间可以对客户端进行身份验证的任何特定方式。WebSocket
服务器可以使用通用HTTP服务器可用的任何客户端身份验证机制,例如cookie,HTTP身份验证或TLS
身份验证
3. websocket握手
Sec-WebSocket-Key头用于确保服务器不接受来自非 WebSocket客户端的连接
3.1 客户端握手请求
标准的http版本必须是1.1或者更高,且请求方法必须为GET 示例:
GET /chat HTTP/1.1
Host: a.com:5000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXdsZSBub25jZQ==
Sec-WebSocket-Version: 13
如果任何header未被识别或者传递了不正确的值,服务器应该发送 400 Bad Request 并立即关闭套接字。如果服务器不理解该版本的WebSockets 他应该发 Sec-WebSocket-Version回一个包含它理解的版本的头。
重点注意:客户端header中的 Sec-WebSocket-Key 是客户端连接服务端的唯一标志,每个发起的websocket都会带着这个唯一标志,没有的疑虑拒绝请求
【注意】
》所有的浏览器都会发送Origin 头 可以用此来进行安全检查,然后返回 403 fobidden
》可以根据requst-uri 在一台服务器上发布多个服务,/chat 可以聊天 /game 可以游戏
》常规的http状态码 只能在握手之前使用
3.2 服务端握手响应
当他接收到请求时,服务器应该发送一个非常奇怪(但仍是HTTP)的响应,就像下面这样(每个header
行后面必须加上\r\n 且在最后一个之后加一个额外的\r\n)
HTTP/1.1 101 Switching Protocols\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
现在我们说一下关键的客户端header中的 Sec-WebSocket-Key 和服务端响应header中的 Sec-WebSocket-Accept
服务器必须从客户端发送的 Sec-WebSocket-Key 派生出来得到 Sec-WebSocket-Accept,
根据客户端生成的 Sec-WebSocket-Key 和 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 连接到一起, 先使用 SHA-1 算法hash这个值 , 然后使用base64编码这个hash.如下:
$key = base64_encode(pack(
'H*',
sha1($matches[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
));
一旦服务器发送了这些头文件,握手就完成了,我们就可以交换数据了,注意这里的 key 是固定的打包方式
【注意】
在发送回复握手之前,服务器可以发送其他头信息比如 Set-Cookie 或者使用其他状态码请求身份验证等