了解两者的区别和用途
如果是写后端的,或者服务器的,肯定都知道socket是什么,套接字,其实就是对TCP和UDP协议封装的接口,相当于是一个库,提供很多函数接口API供我们使用。
我们写的socket后端一般都是基于TCP/IP的,所以socket可以说是属于七层网络中的传输层。
TCP会有握手的过程
平时很多应用软件或者游戏服务器都可以基于socket进行通信,但是当我们要做一个web应用或者想通过浏览器和服务器进行通信的时候,你会发现你根本找不到一个用系统底层socket来通信的方法和例子,也就是说web肯本就不支持直接调用底层socket,这就很奇怪了。
浏览器不支持的socket的原因是不安全,本来你的机器上有防火墙来监听机器的每一个网络IO来防止攻击,但是如果你通过socket来和外部建立了通信,这些监听和防控措施就失效了,外部可以通过socket来做一些不为人知的操作,因为socket是无状态的,没有鉴权或者其他鉴别身份的方式。
例如像DDOS的攻击在socket上也会变得更容易进行和更高效
另一个原因是目前javascript也没有实现socket并提供接口
所以浏览器基本都统一使用http协议,这是在应用层的协议,封装在tcp和ip之上,加入一系列关键字来实现状态。这样的协议更安全了,但是仍然还有个问题,http不是全双工的,也不是基于长连接的,虽然http可以设置关键字keepalive来保证长连接,但是全双工的问题没有解决,服务器无法主动给客户端发送消息,只能由客户端来发起,然后服务端响应。
http为了能实现全双工,可以采用几种方法:
在上面第三种情况下websocket就出现了。
websocket和http是同一层的协议,其实socket本来就算是“长”连接,也是全双工,不过http为了场景应用,在关闭网页时就断开连接,设计成了基于短连接的(现在http1.1也设计成默认长连接了)。websocket也是封装于TCP之上的,websocket的握手过程首先也会有TCP握手的过程,然后进行两次Http请求就完成握手。
使用js客户端连接服务器,用wireshark来抓包分析
可以看到上面红色圈出来的是TCP三次握手的过程,后面绿色的两次HTTP请求是websocket独有的握手过程,我们分别看看里面是什么内容
客户端发送给服务端的HTTP请求:
其中包含一些字段
GET / HTTP/1.1\r\n
Host: ip:port\r\n
Connection: Upgrade\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36\r\n
Upgrade: websocket\r\n
Origin: http://ip:port\r\n
Sec-WebSocket-Version: 13\r\n
Accept-Encoding: gzip, deflate\r\n
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,zh-TW;q=0.6\r\n
Cookie: express_sid=s%3ADe4N5jlcztTKSi5AWM9M00KdcO-5oJg3.YZ5eEfmR6D3bwVGHEB0vgAY9YKqIaACjNOvhJpxLaZ8; prefsHttp={}; token=t.BytEywMsCOqLBA9TMecL\r\n
Sec-WebSocket-Key: JZbp68YQb6yjCwxCcSD2mA==\r\n
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n
\r\n
首先这个请求必须是HTTP请求的GET方式,然后其他一些是HTTP常见的字段,关键的字段在
Connection: Upgrade\r\n
Upgrade: websocket\r\n
Sec-WebSocket-Version: 13\r\n
Sec-WebSocket-Key: JZbp68YQb6yjCwxCcSD2mA==\r\n
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n
\r\n
Connection表示浏览器通知服务器,如果可以的话,就升级到 WebSocket 协议
Upgrade字段表示将通信协议从HTTP/1.1转向该字段指定的协议websocket
Sec-WebSocket-Version用于指定websocket对应的版本
Sec-WebSocket-Key则是用于握手协议的密钥,是 Base64 编码的16字节随机字符串
Sec-WebSocket-Extensions指定服务器可用的协议层插件,可以用列表的形式,分号分隔
HTTP/1.1 101 Switching Protocols\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Sec-WebSocket-Accept: zSbvfZu5ePbUoQVwyv+fAcM2T9U=\r\n
Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover; server_no_context_takeover\r\n
uWebSockets: 20\r\n
有几个关键字段
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Sec-WebSocket-Accept: zSbvfZu5ePbUoQVwyv+fAcM2T9U=\r\n
Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover; server_no_context_takeover\r\n
uWebSockets: 20\r\n
Connection和Upgrade上面解释了
Sec-WebSocket-Accept是服务器在浏览器提供的Sec-WebSocket-Key字符串后面,添加 RFC6456 标准规定的“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”字符串,然后再取 SHA-1 的哈希值,浏览器将对这个值进行验证,以证明确实是目标服务器回应了 WebSocket 请求
server_no_context_takeover是chrome浏览器可以接收的插件字段
uWebSockets是我服务端返回的服务器版本,可以忽略
经过这两个请求后,websocket就握手完成,后面的数据会经过TCP进行传输
所以可以看到,websocket和socket的区别是多了两个http请求验证,websocket和http是类似的协议,但是websocket是长连接