WebSocket & socket.io
WebSocket
当我们创建聊天界面,需要建立客户端和服务器的双向连接时,这时,我们就需要使用到WebSocket,WebSocket 对象提供了用于创建和管理 WebSocket 连接,以及可以通过该连接发送和接收数据的 API,来帮助我们实现聊天对话的功能。
构造函数
**WebSocket()**构造函器会返回一个 WebSocket对象。
1 | var aWebSocket = new WebSocket(url [, protocols]); |
参数
url要连接的 URL;这应该是 WebSocket 服务器将响应的 URL。
protocols(可选)一个协议字符串或者一个包含协议字符串的数组。这些字符串用于指定子协议,这样单个服务器可以实现多个 WebSocket 子协议(例如,你可能希望一台服务器能够根据指定的协议(
protocol)处理不同类型的交互)。如果不指定协议字符串,则假定为空字符串。
实例属性
WebSocket.readyState
返回当前 WebSocket的链接状态,只读。
1 | var readyState = WebSocket.readyState; |
返回的值
以下其中之一
0(
WebSocket.CONNECTING)正在链接中
1 (
WebSocket.OPEN)已经链接并且可以通讯
2 (
WebSocket.CLOSING)连接正在关闭
3 (
WebSocket.CLOSED)连接已关闭或者没有链接成功
实例方法
WebSocket.close()
WebSocket.close() 方法关闭 WebSocket 连接或连接尝试(如果有的话)。如果连接已经关闭,则此方法不执行任何操作。
语法
1 | WebSocket.close(); |
参数
code可选一个数字状态码,它解释了连接关闭的原因。如果没有传这个参数,默认使用 1005。
CloseEvent的允许的状态码见状态码列表。reason可选一个人类可读的字符串,它解释了连接关闭的原因。这个 UTF-8 编码的字符串不能超过 123 个字节。
抛出的异常
INVALID_ACCESS_ERR一个无效的
codeSYNTAX_ERRreason字符串太长(超过 123 字节)
WebSocket.send()
WebSocket.send() 方法将需要通过 WebSocket 链接传输至服务器的数据排入队列,并根据所需要传输的 data bytes 的大小来增加 bufferedAmount的值。若数据无法传输(例如数据需要缓存而缓冲区已满)时,套接字会自行关闭。
语法
1 | WebSocket.send("Hello server!"); |
参数
data用于传输至服务器的数据。它必须是以下类型之一:
USVString文本字符串。字符串将以 UTF-8 格式添加到缓冲区,并且
bufferedAmount将加上该字符串以 UTF-8 格式编码时的字节数的值。ArrayBuffer你可以使用一有类型的数组对象发送底层二进制数据;其二进制数据内存将被缓存于缓冲区,
bufferedAmount将加上所需字节数的值。BlobBlob类型将队列 blob 中的原始数据以二进制中传输。bufferedAmount将加上原始数据的字节数的值。ArrayBufferView你可以以二进制帧的形式发送任何 JavaScript 类数组对象 ;其二进制数据内容将被队列于缓冲区中。值
bufferedAmount将加上必要字节数的值。
异常
INVALID_STATE_ERR当前连接未处于
OPEN状态。SYNTAX_ERR数据是一个包含未配对代理 (unpaired surrogates) 的字符串。
事件
WebSocket.onclose
WebSocket.onclose 属性返回一个事件监听器,这个事件监听器将在 WebSocket 连接的readyState 变为 CLOSED时被调用,它接收一个名字为“close”的 CloseEvent 事件。
语法
1 | WebSocket.onclose = function(event) { |
WebSocket: 错误事件
当websocket的连接由于一些错误事件的发生 (例如无法发送一些数据) 而被关闭时,一个error事件将被引发。
1 | // Create WebSocket connection |
WebSocket: message event
message 事件会在 WebSocket 接收到新消息时被触发。
1 | // 创建一个 WebSocket 连接 |
WebSocket: 建立连接
open 事件会在 WebSocket 建立连接时被触发。
1 | // Connection opened |
使用了WebSocket的基本样例
服务器
1 | //server.js |
客户端
1 | <!--index.html--> |
使用了socket.io的基本样例
服务器
1 | //server.js |
客户端
1 | <!--index.html--> |
1 | <script src="/socket.io/socket.io.js"></script> |
注意:<script src="/socket.io/socket.io.js"></script> 这段代码表示从服务器的 /socket.io 路径下获取 socket.io.js 文件,并在客户端加载该文件,以便使用 Socket.IO 客户端功能。具体的文件路径和名称可能会因服务器配置和部署方式而有所不同。因为这个服务器和客户端在一个接口,所以可以正常使用
在vue中使用socket.io的基本样例
服务器
1 | //server.js |
注意1:CORS策略是一种浏览器安全机制,用于限制跨域请求。在使用跨域请求时,一定要在服务器上配置正确的CORS头信息,以允许来自客户端http://localhost:5173的跨域请求。
注意2:但我直接将express函数创建的app传给socket.io包的Server函数时,发生了报错
1 | const express = require('express'); //这里使用了express框架 |
1 | Error: You are trying to attach socket.io to an express request handler function. Please pass a http.Server instance. |
虽然 Express 构建在 http.Server 之上,但它并不是 http.Server 的直接实例。在使用 socket.io 时,需要将 http.Server 实例作为参数传递给 socket.io 构造函数,而不是 Express 应用程序实例。
但是这样写也可以实现服务器功能,不使用Express框架,直接使用http创建http实例
1 | const http = require('http'); // 导入 http 模块 |
这两种写法的区别在于 HTTP 服务器实例的创建方式不同:
第一种写法:
1
2
3
4
5
6
7
8const http = require('http');
const server = http.createServer();
const { Server } = require("socket.io");
const io = new Server(server, {
cors: {
origin: "http://localhost:5173"
}
});在这种写法中,使用了
http.createServer()方法创建了一个空的 HTTP 服务器实例server,然后将其传递给socket.io的Server类的构造函数来创建socket.io服务器实例io。第二种写法:
1
2
3
4
5
6
7
8
9
10const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server, {
cors: {
origin: "http://localhost:5173"
}
});在这种写法中,除了导入
http模块外,还导入了express框架。首先创建了一个 Express 应用程序实例app,然后使用http.createServer(app)方法将其与 HTTP 服务器实例server关联,最后将server实例传递给socket.io的Server类的构造函数来创建socket.io服务器实例io。
这两种写法的区别在于是否使用了 Express 应用程序。如果你的项目中已经使用了 Express 框架,并且希望将 WebSocket 连接集成到 Express 应用程序中,那么第二种写法是更常见和推荐的方式。它允许你在同一个应用程序中处理 HTTP 请求和 WebSocket 连接,并共享同一个端口。
如果你只需要一个独立的 HTTP 服务器实例,并且不需要与 Express 应用程序进行集成,那么第一种写法就足够了。
总结:
- 第一种写法创建了一个独立的空的 HTTP 服务器实例,适用于不需要与 Express 应用程序集成的情况。
- 第二种写法使用了 Express 框架,将 Express 应用程序与 HTTP 服务器实例关联,适用于在同一个应用程序中处理 HTTP 请求和 WebSocket 连接的情况。这种方式更常见和推荐,因为它提供了更多的灵活性和集成性。
客户端
1 | //socket.js |
1 | //在vue组件中调用这个api |
1 | socket.timeout(5000).emit("create-something", value.value, ()=>{ |
在这段代码中:
socket.timeout(5000):通过socket.timeout()方法设置了一个超时时间为5秒。这意味着如果在5秒内没有收到服务器对该事件的响应,将触发超时处理。emit("create-something", value.value, () => { ... }):使用.emit()方法发送一个名为”create-something”的自定义事件,并传递value作为事件的数据参数。同时,提供了一个回调函数作为最后一个参数。- 当服务器接收到”create-something”事件并处理完成后,可以选择通过回调函数来执行额外的逻辑。
- 在这段代码中,回调函数简单地将
this.isLoading设置为false,表示请求已完成,加载状态被置为false。
- 这里推荐使用
socket.emit,因为:emit方法:emit方法用于发送具有自定义事件名称的消息。它可以用于发送任意类型的数据,并且可以附带回调函数来处理服务器对该消息的响应。send方法:send方法是 Socket.IO 的一种简化形式,用于发送文本消息。它只能发送字符串类型的数据,而无法发送复杂的 JavaScript 对象。与emit方法不同,send方法不需要指定事件名称,它将使用默认的 “message” 事件。因此,send方法适用于简单的文本消息传递,而不需要自定义事件或复杂的数据。


