WS/WSS 协议入门及简单反代

经验 暂无评论

因需要使用客服IM系统,需要使用 WebSocket 协议,因此简单了解和使用,记录为此文。

背景

网站为了实现推送技术,所用的技术一般都是轮询。轮询是在特定的时间间隔(比如每秒一次)由浏览器对服务器发出 HTTP 请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式一个很明显的缺点,就是浏览器需要不断的向服务器发出请求,然而 HTTP 请求都是完整的请求,不支持增量信息传输,因此传输中大部分可能是较长的头部信息,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多服务器资源。

在这种情况下,WebSocket 横空出世,HTML5 定义了 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

简介

WebSocket 是一种在单个 TCP 连接上进行全双工通讯的协议。使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket 是一种与 HTTP 不同的协议。两者都位于 OSI 模型的应用层,并且都依赖于传输层的 TCP 协议。虽然两者不同,但是 RFC 6455 中规定 WebSocket 默认通过 HTTP 端口 80 和 443 进行工作,并支持 HTTP 代理和中继,从而使其与 HTTP 协议兼容。为了实现兼容性,WebSocket 握手使用 HTTP Upgrade 请求头对 HTTP 协议更改为 WebSocket协议。

与 HTTP 不同,WebSocket 提供全双工通信。此外,WebSocket 还可以在 TCP 之上实现消息流。TCP 单独处理字节流,没有固有的消息概念。

WebSocket 协议规范将 ws(WebSocket) 和 wss(WebSocket Secure) 定义为两个新的 URI(统一资源标识符)方案,分别对应明文和加密连接(类似于 HTTP 与 HTTPS 的关系)。除了方案名称和片段ID(不支持#)之外,其余的URI组件都被定义为此 URI 的通用语法。

需要注意的是若网站使用了 HTTPS,则嵌套其中的 WS 协议会被屏蔽,必须使用 WSS 协议,具体报错如下

Mixed Content: The page at 'https://domain.com/' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://x.x.x.x:xxxx/'. This request has been blocked; this endpoint must be available over WSS.

小贴士:如果使用了插件或者 JS 等方式调用 WS 协议,需要全部改为 WSS 协议。

由于一般情况下 WebSocket 会默认调用 IP 方式,因此仅仅修改 URI 后可能会看到以下报错,这是由于证书和域名不匹配导致的。

WebSocket connection to 'wss://172.16.4.111:4232/websocket' failed: Error in connection establishment: net::ERR_SSL_PROTOCOL_ERROR

反代

为了美观和访问方便,推荐用 WEB 服务对 WebSocket 进行反代,可以美化路径,同时可以完成 WSS 协议的过渡。

location /chat/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

反代后,直接调用 wss://www.domain.com/websocket 即可,在访问时可见其状态码为 101 Switching Protocols 且在响应头中可见字段 Connection: upgrade

小贴士:由于反向代理默认继承 proxy_read_timeout 属性,因此在默认情况下最长连接时长为一分钟,若对其有需求进行修改,则需要对其重新定义。

复杂

下面为一个复杂示例,会自动检测客户端的请求头是否包含 "Upgrade" 字段

http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    server {
        ...

        location /chat/ {
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
        }
    }

附录

参考链接

如果遇到问题或者对文章内容存疑,请在下方留言,博主看到后将及时回复,谢谢!
回复
回答22+28=