html 自动连接websocket_websocket 是怎样连接的

背景

最近项目新增了一个 websocket 服务,用 nginx 做了一个简单的端口转发,然后调用的时候发现报错:

error: Unexpected server response: 426

搜索引擎走一波,找到几篇相关文章:

https://nginx.org/en/docs/http/websocket.html

https://www.nginx.com/blog/websocket-nginx

解决方式也很简单,根据第一篇文章的说明,只要增加转发响应头的配置:

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection 'upgrade';



因为 websocket 的连接建立是基于 HTTP/1.1 的,所以有必要制定 http 协议:

proxy_http_version: 1.1

当然这不是必须的,如果服务默认是 HTTP/1.1 的话



具体配置如下:

http {    map $http_upgrade $connection_upgrade {        default upgrade;        '' close;    }     upstream websocket {        server 192.168.100.10:8010;    }     server {        listen 8020;        location / {            proxy_pass http://websocket;            proxy_http_version 1.1;            proxy_set_header Upgrade $http_upgrade;            proxy_set_header Connection $connection_upgrade;            proxy_set_header Host $host;        }    }}

但多年踩坑经验告诉我,如果不了解某项技术的细节,早晚还要掉进下一个坑。所以打算好好研究下这里面的执行逻辑。

Websocket 建立连接的过程分析

Http1.1 支持协议转换机制,具体说明参考 https://tools.ietf.org/html/rfc2616#section-14.42

下图是 websocket 建立连接-数据传输-断开连接 的整个过程。

85a7229252db8fe9640099fad4fabca3.png
  1. 首先客户端发起 GET 请求
501ca6adcf96e2cd7db538ff3a2ea978.png

这里主要关注 Connection 和 Upgrade 两个请求头:

  • 设置 Connection 头的值为 Upgrade 来指示这是一个升级请求
  • Upgrade 头制定要升级到的协议



  1. 服务端响应

如果服务端决定升级这次连接,就会返回 101 Switching Protocols 响应状态

d7ff24f1185345027029189523ecdbcb.png

经过以上过程,Websocket 的连接就建立成功了

但为什么nginx 代理需要明确配置 Connection 和 Upgrade 请求头呢?

原来,http 协议将消息头分为了两类:end-to-end 和 hop-by-hop,具体说明参考:https://tools.ietf.org/html/rfc2616#section-13.5.1

他们的特性如下:

  • end-to-end
  • 该类型的消息头会被代理转发
  • 该类型的消息头会被缓存
  • hop-to-hop:
  • 该类型的消息头不会被代理转发
  • 该类型的消息头不会被缓存

这样就不难理解,nginx 配置代理转发后,默认并不能转发 Connection 和 Upgrade 消息头,这样转发后的请求到达 Websockt 服务后就没有这两个头,因此就不能由 http 协议升级为 websocket 协议。因此需要在 nginx 中进行手动配置。



以下为 HTTP/1.1 中 hop-to-hop 类型的消息头:Connection, Keep-Alive, Proxy-Authenticate, Proxy-Authorization, TE, Trailers, Transfer-Encoding, Upgrade

连接为什么断开了

从上面的抓包可以看到,最终 websocket 建立的连接被自动断开了,文档中也做出了解释,这是 nginx 的机制。

默认情况下,如果被代理的服务 60 秒内没有进行数据传输,连接就会被断开。这个超时时间可以通过 proxy_read_timeout 指令设置。

如果要保持建立的连接不断开,就要通过心跳包等手段进行维持。

注意

  1. 该升级机制只是 HTTP/1.1 有效,HTTP/2 已不支持该机制
  2. nginx 会断开长时间没有数据传输的连接
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
class ChatConsumer(AsyncWebsocketConsumer): async def websocket_connect(self, message): # 接收客户端请求 # self.accept() await self.channel_layer.group_add( "chat_group", self.channel_name, ) await self.accept() await self.send("连接") async def websocket_receive(self, message, ): print(message) await self.send("已收到") print("收到") check = message.get('check') print(check) file_data = message.get('file') print(file_data) # file_data = r'D:\python study\web01\website01\static\file\实验数据.xlsx' await self.send('正在打开数据...') # df = pd.read_excel('received_file.xlsx',engine='openpyxl') df = pd.read_excel(file_data) print("结束") def websocket_disconnect(self, message): print("断开连接") raise StopConsumer() socket = new WebSocket("ws://127.0.0.1:8000/room/123/"); //当websocket接收到服务端发来的消息时,自动触发这个函数 socket.onmessage = function (event) { let tag = document.createElement("div") tag.innerText = event.data document.getElementById("message").appendChild(tag); console.log(tag) }def index(request): if request.method == "GET": return render(request, 'index.html') if request.method == "POST": file = request.FILES.get("file") check = request.POST.get('check') # 获取Channel层对象 channel_layer = get_channel_layer() # 发送消息到ChatConsumer的websocket_receive方法 async_to_sync(channel_layer.group_send)('chat_group', { 'type': 'websocket.receive', 'file': file, 'check': check, }) # main(file, check, 15) return render(request, 'index.html') 前端弹窗中可以接收def websocket_connect的self.send但却接收不到 websocket_receive中的self.send内容,但websocket_receive中的print都可以正常运行,是怎么回事?
07-17

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值