由于socketio是借用nextjs启动的服务进行创建的,在app router下无法在端口处获取到完整的req与res
目前的nextJS版本(13.3+)
可以看见使用app router时,response对象并不会在这里进行完整返回,而是在nextJS内部
那么就需要使用page router,处理socket连接,使用page router可以获取到完整的req与res
WebSocket
wiki
WebSocket是一种网络传输协议,可在单个TCP连接上进行全双工通信,位于OSI模型的应用层。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以建立持久性的连接,并进行双向数据传输。
为了实现兼容性,WebSocket握手使用HTTP Upgrade从HTTP协议更改为WebSocket协议。
默认情况下,Websocket协议使用80端口;运行在TLS之上时,默认使用443端口。
握手协议
WebSocket 是独立的、建立在TCP上的协议。
Websocket 通过 HTTP/1.1 协议的101状态码进行握手。
为了建立Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(Handshaking)。
一个典型的Websocket握手请求如下
客户端请求:
服务器回应:
字段说明
Connection必须设置Upgrade,表示客户端希望连接升级。
Upgrade字段必须设置Websocket,表示希望升级到Websocket协议。
Sec-WebSocket-Key是随机的字符串,服务器端会用这些数据来构造出一个SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一个特殊字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后计算SHA-1摘要,之后进行Base64编码,将结果做为“Sec-WebSocket-Accept”头的值,返回给客户端。如此操作,可以尽量避免普通HTTP请求被误认为Websocket协议。
Sec-WebSocket-Version 表示支持的Websocket版本。RFC6455要求使用的版本是13,之前草案的版本均应当弃用。
Origin字段是必须的。如果缺少origin字段,WebSocket服务器需要回复HTTP 403 状态码(禁止访问)。
其他一些定义在HTTP协议中的字段,如Cookie等,也可以在Websocket中使用。
可以看出握手协议的时候需要计算出Sec-WebSocket-Accept的值,所以一般是直接使用第三方库
Socket.IO
Socket.IO在服务器端有多种构造方式,在这里由于是需要借用nextJS启动的服务所有使用new Server(httpServer[, options])
的方式
里面的httpServer可以使用上面req或res里面的socket对象下的server
创建后再将实例存到socket对象上,下次请求就直接判断有没有这个值,有的话就说明当前请求里已经进行了WebSocket连接,直接返回
在创建ServerIO实例时,如果绑定了server,并配置了path,那么下次请求这个路径,Socket.io 会自动处理 WebSocket 握手,如果不是WebSocket握手而是其他方法则会返回
其他
客户端做了降级处理,如果websocket连接失败,那么就使用长轮询模拟 https://socket.io/zh-CN/docs/v4/client-options/#transports
socketio默认的握手连接顺序与websocket的不同,socketio的101握手不是直接就进行的,而是需要通过3个轮询请求后,再发起,当101握手成功后,就过一段时间再发起关闭轮询的请求,完成整套操作一共需要请求5次
- 握手 (包含会话 ID — 此处会返回一个sid:lx4TP…AAG — 用于后续请求)
- 发送数据 (HTTP 长轮询)
- 接收数据 (HTTP 长轮询)
- 升级 (WebSocket)
- 接收数据 (HTTP 长轮询, WebSocket连接建立成功后关闭)
连接时心跳机制
问题
在NextJS版本大于13.2.5时会出现连接不上的问题
一直尝试ws握手失败
经过查询发现可以使用自定义服务启动时进行设置
https://stackoverflow.com/questions/76277148/using-socket-io-with-next-js
可是还是不行,虽然这个握手操作能返回101但是会直接断开连接
发现是nextjs14+的版本对于没有对upgrade请求的监听导致这个ws握手一直失败
官方提供了一个Draft
截止当前最新版本(13.4.20-canary.15),还没有用!!!
但是发现个现象,第一次连接会失败,然后重启服务,不刷新页面,居然就可以了
只要一刷新页面就不行了
总结
当前NextJS与Socket.IO开发还有缺陷,无法使用WebSocket进行通信,只能使用轮询方式进行通信
暂时就使用轮询进行开发