V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
zhoudaiyu
V2EX  ›  问与答

有几个关于 websocket 的疑问想问问大家

  •  
  •   zhoudaiyu · 2021-06-02 14:58:55 +08:00 · 1429 次点击
    这是一个创建于 1273 天前的主题,其中的信息可能已经有所发展或是发生改变。

    1 、websocket 连接为什么需要握手?为什么不能像普通的 http 请求那样直接连接? 2 、握手为什么要借助 http 请求才能完成?而不是通过 tcp 等协议? 3 、既然 websocket 协议只是借助 http 协议建立连接,那么为什么 nginx 要把 websocket 的相关配置也放在 http 配置块中?我理解 nginx 只在在首次 ws 握手时在请求头里面加几个字段(关于 websocket 的)就行了,后面的连接都不是 http 的了,那建立 ws 连接完毕后 nginx 在代理什么呢? 4 、如果请求的链路是客户端->硬件负载均衡->nginx->ws 应用服务器那么是不是硬件负载均衡只做一个纯 4 层的转发就好了,要是基于 http 的就会出问题吧?可能会丢掉 ws 的包 5 、有通过 http 的握手为啥挥手不通过 http 了?

    10 条回复    2021-06-03 00:59:02 +08:00
    ysc3839
        1
    ysc3839  
       2021-06-02 15:30:03 +08:00   ❤️ 1
    1, 2. 我猜测可能是为了复用 HTTP 的端口,以及使用 HTTP 的一些功能。
    4. 是的,基于 HTTP 的反向代理,如果没专门支持 WebSocket 的话应该是没法正常工作的。
    5. 因为已经升级成为了 WebSocket,不再是 HTTP 了。
    GuuJiang
        2
    GuuJiang  
       2021-06-02 15:41:42 +08:00   ❤️ 7
    1. 从历史进程来看,websocket 是晚于 http 诞生的,你想象下如果自己就是制定标准的那班哥们,现在面临一个新任务,需要设计一种浏览器能使用的,能够保持连接,且服务端能够主动发送数据的协议,那么你有两种选择,一种是基于 TCP 设计一个全新的协议,一种是看看能否在现有的 http 的基础上 hack 一下完成目标,于是一拍大腿,http 本来就基于 TCP,把这个现成的连接拿来用不就完事了,于是只需要设计出一种方法,通过一个 http 请求让双方都知道“现在我给你发了一个 http 请求,你处理完这个请求后咱们这次请求时使用的那个 TCP 连接先别关,且从现在开始,在这个连接上咱们开始玩一个新的协议,这个协议不如就叫它 ws 协议吧”,这就是第一个 http 请求里的“Connection: Upgrade”干的事,这样有个很大的好处,就是当服务端版本比较旧,还不支持 websocket 时,它收到的仍然是一个合法的 http 请求,只不过忽略了“Connection: Upgrade”而已,这样不会带来什么大的兼容性问题
    2. 原因同上
    3. 同上,这时候 nginx 相当于个中间人,于是它需要干两件事,一是自己处理“Connection: Upgrade”,将上游到 nginx 的连接切换为 websocket,二是把“Connection: Upgrade”忠实地传递下去,让下游能够处理,从而将 nginx 到下游的连接也切换为 websocket,当存在多级 nginx 时同理,每一级都要正确地完成这两件事,整个链条才能串起来
    4. 是可以的,如果做 4 层转发那么上面完全透明,而如果是应用层,就像 nginx 这样,那就需要像上面提到的这样,不光耀转发 Upgrade,同时自己也需要能够响应 Upgrade
    5. 在应用层最好不要随便使用“挥手”这个词,以免带来更多的误解,虽然我知道你真正想表达什么,答案也很简单,因为从处理完 Upgrade 那一刻开始,协议已经切换成 websocket 了
    zhoudaiyu
        3
    zhoudaiyu  
    OP
       2021-06-02 16:00:27 +08:00
    @GuuJiang 我对 ws 理解好像是有点问题,websocket 是不是通过 http 长连接打开了个类似隧道的东西,然后通过这个“隧道”发送数据?因为我从 chrome 里看 ws 通过 http 的握手结束了但是这次 http 请求并没有结束,只有当我主动关闭了 ws 后 http 连接才结束。
    zhoudaiyu
        4
    zhoudaiyu  
    OP
       2021-06-02 16:00:57 +08:00
    @ysc3839 我对 ws 理解好像是有点问题,websocket 是不是通过 http 长连接打开了个类似隧道的东西,然后通过这个“隧道”发送数据?因为我从 chrome 里看 ws 通过 http 的握手结束了但是这次 http 请求并没有结束,只有当我主动关闭了 ws 后 http 连接才结束。
    ysc3839
        5
    ysc3839  
       2021-06-02 16:03:57 +08:00
    @zhoudaiyu WebSocket 是使用 HTTP/1.1 的协议升级机制,升级后就不再传输 HTTP 协议的数据了,变成了一个 TCP 连接了。也可以理解成是一个基于 TCP 的“隧道”。
    ysc3839
        6
    ysc3839  
       2021-06-02 16:06:04 +08:00
    @ysc3839 更正一下,严格来说反向代理不需要专门支持 WebSocket 协议,只需要支持 HTTP/1.1 的 upgrade 功能即可。因为 upgrade 之后就可以看成是一个 TCP 隧道,反向代理直接转发其中数据就行了,不需要关心其中的数据是不是 WebSocket 。
    GuuJiang
        7
    GuuJiang  
       2021-06-02 19:22:20 +08:00
    @zhoudaiyu 你这样一说就明白你的疑惑来自哪里了,实际情况是,发送“握手”使用的那个 http 总得要先有个 tcp 连接对吧,姑且称他为 A,“握手”的请求并不是为了开启一个新的连接 B,而是接着使用 A,只不过在 A 上承载的应用层协议摇身一变切换为了 ws 协议了
    zhoudaiyu
        8
    zhoudaiyu  
    OP
       2021-06-02 20:30:58 +08:00 via iPhone
    @GuuJiang 那到底是 http 承载的 ws 还是 TCP 直接承载的呢?我看 wireshark 是 TCP 直接承载 WS 的连接
    GuuJiang
        9
    GuuJiang  
       2021-06-02 20:51:24 +08:00
    @zhoudaiyu 后者
    jim9606
        10
    jim9606  
       2021-06-03 00:59:02 +08:00
    题主要明白 Websocket 有很多你看起来很多余的设计,是因为设计者希望 Websocket 能跟完全不支持 Websocket 的网络中间件(防火墙、代理)兼容,所以就有:
    1. 必须使用 HTTP Upgrade 握手,因为 Upgrade 语义早在 HTTP1.1 就被定义了,标准定义 Upgrade 之后的流内容就不是 HTTP1.1 了,HTTP2 采用相同方法进行升级。正确实现 HTTP1.1 Upgrade 语义的网络中间件无需修改即可让 Websocket 穿透
    2. Upgrade 头通过 Sec-WebSocket-Key Sec-Websocket-Accept 头对暗号,确保两头都真正支持 Websocket 而不是一个只会 copy 头的哑主机
    3. 数据必须经过 MASK,防止使用关键字匹配的网络中间件意外篡改数据流
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1059 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 22:26 · PVG 06:26 · LAX 14:26 · JFK 17:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.