本文档介绍无服务器支持的详细信息。 由于 Socket.IO 支持包括无服务器支持高度依赖于 Web PubSub 服务的现有接口,因此引入了许多复杂的转换和映射。 对于大多数用户,我们建议将 Azure 函数绑定与无服务器模式结合使用。 您可以逐步学习教程 教程:在无服务器模式下使用 Azure 函数构建聊天应用
生命周期工作流
在 Socket.IO 无服务器模式下,客户端的连接生命周期通过持久连接和 Webhook 的组合进行管理。 工作流可确保无服务器体系结构可以有效地处理实时通信,同时保持对客户端连接状态的控制。
套接字连接
在客户端中,应使用 Socket.IO 兼容的客户端。 在以下客户端代码中,我们使用 官方 JavaScript 客户端 SDK。
客户:
// Initiate a socket
var socket = io("<service-endpoint>", {
path: "/clients/socketio/hubs/<hub-name>",
query: { access_token: "<access-token>"}
});
// handle the connection to the namespace
socket.on("connect", () => {
// ...
});
上一示例的说明:
-
<service-endpoint>是服务资源的Endpoint。 - 在
path中,<hub-name>是 Web PubSub for Socket.IO 的概念,该概念提供集线器之间的隔离。 -
<access-token>这是一个 JWT,用于向服务进行身份验证。 有关详细信息,请参阅(如何生成访问令牌)。
身份验证流
当客户端尝试连接到服务时,该过程分为两个不同的步骤:建立 Engine.IO(物理)连接并连接到命名空间,该命名空间称为 Socket.IO 术语中的套接字。 身份验证过程在这两个步骤之间有所不同:
Engine.IO 连接:在此步骤中,服务使用访问令牌对客户端进行身份验证,以确定是否接受连接。 如果相应的中心配置为允许匿名模式,则 Engine.IO 连接可以继续,而无需验证访问令牌。 但是,出于安全原因,我们建议在生产环境中禁用匿名模式。
Engine.IO 连接 URL 遵循格式。 但在大多数情况下,它应由 Socket.IO 客户端库处理。
http://<service-endpoint>/clients/socketio/hubs/<hub-name>/?access_token=<access-token>可在此处找到访问令牌的详细信息
套接字:成功建立 Engine.IO 连接后,客户端 SDK 会发送负载以连接到该命名空间。 服务收到套接字连接请求后,服务将触发对事件处理程序的连接调用。 此步骤的结果取决于连接响应返回的状态代码:200 状态代码指示已批准套接字,而 4xx 或 5xx 状态代码会导致套接字被拒绝。
连接套接字后,服务将触发对事件处理程序的连接调用。 它是一个异步调用,用于通知事件处理程序套接字已成功连接。
发送消息
客户端可以使用以下代码发送消息:
socket.emit("hello", "world");
在此示例中,消息使用 EventName “hello”发送,后续参数是参数。 该服务触发具有相同事件名称的相应用户事件。 这是一个同步调用,响应数据将返回到客户端不变。 在响应正文中包含确认,以确认已接收和处理消息是常见的做法。
例如,客户端发出带有 ack 的消息:
socket.emit("hello", "world", (response) => {
console.log(response);
});
事件处理程序可以响应正文,例如 { type: ACK, namespace: "/", data: ["bar"], id: 13 } 确认排放。 此响应确认服务器接收和处理消息。
套接字断开连接
客户端与命名空间断开连接或相应的 Engine.IO 连接关闭时,会导致套接字关闭。 服务会为每个断开连接的套接字触发断开事件。 这是用于通知的异步调用。
身份验证详细信息
服务使用持有者令牌进行身份验证。 有两个主要方案可以使用令牌。
发起 Engine.IO 连接。 以下请求是一个示例。
https://<service-endpoint>/clients/socketio/hubs/<hub-name>/?access_token=<access-token>用于发送消息或管理连接的 RESTful 请求。 以下请求是一个示例。
POST {endpoint}/api/hubs/{hub}/:removeFromGroups?api-version=2024-01-01 Headers: Authorization: Bearer <token>
令牌的生成还可以分为两个类别:基于密钥的身份验证或基于标识的身份验证。
基于密钥的身份验证
JWT 格式:
Header
{
"alg": "HS256",
"typ": "JWT"
}
有效负载
{
"nbf": 1726196900,
"exp": 1726197200,
"iat": 1726196900,
"aud": "https://sample.webpubsub.azure.com/api/hubs/hub/groups/0~Lw~/:send?api-version=2024-01-01",
"sub": "userId"
}
aud 应与所请求的 URL 保持一致。
sub 是连接的 userId。 仅适用于 Engine.IO 连接请求。
签名
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), <AccessKey>)
AccessKey可以从服务 Azure 门户或 Azure CLI 获取:
az webpubsub key show -g <resource-group> -n <resource-name>
基于标识的身份验证
RESTful API 的令牌
基于标识的身份验证使用 access token 由Microsoft标识平台签名。
用于请求令牌的应用程序必须使用资源 https://webpubsub.azure.com 或范围 https://webpubsub.azure.com/.default。 它需要被授予 Web PubSub Service Owner 角色。 有关详细信息,请参阅 使用 Microsoft Entra ID 授权访问 Web PubSub 资源
用于 Engine.IO 连接的令牌
与 RESTful API 不同,Engine.IO 连接不直接使用 Microsoft Entra ID 令牌。 相反,必须对服务进行 RESTful 调用以获取令牌,并使用返回的令牌作为客户端的访问令牌。
POST {endpoint}/api/hubs/{hub}/:generateToken?api-version=2024-01-01
Headers:
Authorization: Bearer <Microsoft Entra ID Token>
有关更多可选参数,请参阅 “生成客户端令牌”
支持的功能和 RESTful API 接口
服务器可以使用 RESTful API 来管理 Socket.IO 客户端并向客户端发送消息。 由于 Socket.IO 重复使用 Web PubSub 服务 RESTful API,Socket.IO 术语转换为 Web PubSub 术语。 以下文档详细说明了转换。
关键概念
命名空间、房间和组映射
由于 Socket.IO 具有术语命名空间和房间,而 Web PubSub 服务则没有,我们将命名空间和房间映射到组。
Group name <--> 0~Base64UrlEncoded(namespace)~Base64UrlEncoded(room)
表示整个命名空间:
Group name <--> 0~Base64UrlEncoded(namespace)~
请参阅 Base64URL Standard,它是一种 base64 协议,能够将编码结果用作文件名或 URL 地址。
例如:
Namespace = /, Room = rm <--> Group = 0~Lw~cm0
Namespace = /ns, Room = rm <--> Group = 0~L25z~cm0
Namespace = /ns <--> Group = 0~L25z~
连接 ID
连接 ID 唯一标识 Engine.IO 连接。 在同一 Engine.IO 连接上运行的不同套接字共享相同的连接 ID。
套接字 ID
套接字 ID 唯一标识套接字连接。 根据 Socket.IO 规范,每个套接字会自动加入与其套接字 ID 同名的聊天室。 例如,套接字 ID 为“abc”的套接字会自动放置在房间“abc”。这种设计可以通过将房间名称与套接字 ID 相同的对应房间作为目标,从而专门向该套接字发送消息。
将套接字添加到会议室
POST {endpoint}/api/hubs/{hub}/:addToGroups?api-version=2024-01-01
Headers:
Authorization: Bearer <access token>
Content-Type: application/json
请求主体
{
"filter": "An OData filter which target connections satisfy",
"groups": [] // Target group
}
有关 REST 详细信息,请参阅 “向组添加连接 ”。 有关 筛选器详细信息,请参阅 Azure Web PubSub 服务中的 OData 筛选器语法 。
Example
将命名空间socketId中的套接字/ns添加到rm中心myHub空间。
POST {endpoint}/api/hubs/myHub/:addToGroups?api-version=2024-01-01
Headers:
Authorization: Bearer <access token>
Content-Type: application/json
Body:
{
"filter": "'0~L25z~c29ja2V0SWQ' in groups",
"groups": [ "'0~L25z~cm0" ]
}
从会议室中删除套接字
POST {endpoint}/api/hubs/{hub}/:removeFromGroups?api-version=2024-01-01
Headers:
Authorization: Bearer <access token>
Content-Type: application/json
请求主体
{
"filter": "An OData filter which target connections satisfy",
"groups": [] // Target group
}
有关 REST 详细信息,请参阅 “从组中删除连接 ”。 有关 筛选器详细信息,请参阅 Azure Web PubSub 服务中的 OData 筛选器语法 。
Example
在中心myHub的房间rm中,从命名空间/ns中移除套接字socketId。
POST {endpoint}/api/hubs/myHub/:removeFromGroups?api-version=2024-01-01
Headers:
Authorization: Bearer <access token>
Content-Type: application/json
Body:
{
"filter": "'0~L25z~c29ja2V0SWQ' in groups",
"groups": [ "'0~L25z~cm0" ]
}
发送到套接字
POST {endpoint}/api/hubs/{hub}/groups/{group}/:send?api-version=2024-01-01
Headers:
Authorization: Bearer <access token>
Content-Type: text/plain
请求主体
Engine.IO serialized payload
有关 REST 的详细信息,请参阅 发送到全部。 有关 Engine.IO 协议 的详细信息,请参阅 Engine.IO 协议。
Example
将消息{"eventName", "arg1", "arg2"}发送到中心socketId命名空间/ns中的套接字myHub。
客户端可以使用代码处理消息:
socket.on('eventName', (arg1, arg2) => {
// ...
});
POST {endpoint}/api/hubs/myHub/groups/0~L25z~c29ja2V0SWQ/:send?api-version=2024-01-01
Headers:
Authorization: Bearer <access token>
Content-Type: text/plain
Body:
42/ns,["eventName","arg1","arg2"]
发送到房间
POST {endpoint}/api/hubs/{hub}/groups/{group}/:send?api-version=2024-01-01
Headers:
Authorization: Bearer <access token>
Content-Type: text/plain
请求主体
Engine.IO serialized payload
有关 REST 详细信息,请参阅 “发送到全部 ”。 有关 Engine.IO 协议 的详细信息,请参阅 Engine.IO 协议。
Example
将消息{"eventName", "arg1", "arg2"}发送到集线器myHub中命名空间/ns的房间rm中。
客户端可以使用代码处理消息:
socket.on('eventName', (arg1, arg2) => {
// ...
});
POST {endpoint}/api/hubs/myHub/groups/0~L25z~cm0/:send?api-version=2024-01-01
Headers:
Authorization: Bearer <access token>
Content-Type: text/plain
Body:
42/ns,["eventName","arg1","arg2"]
发送到命名空间
POST {endpoint}/api/hubs/{hub}/groups/{group}/:send?api-version=2024-01-01
Headers:
Authorization: Bearer <access token>
Content-Type: text/plain
请求主体
Engine.IO serialized payload
有关 REST 详细信息,请参阅Send To All。 有关 Engine.IO 协议 的详细信息,请参阅 Engine.IO 协议。
Example
将消息{"eventName", "arg1", "arg2"}发送到中心/ns中的命名空间myHub。
客户端可以使用代码处理消息:
socket.on('eventName', (arg1, arg2) => {
// ...
});
POST {endpoint}/api/hubs/myHub/groups/0~L25z~/:send?api-version=2024-01-01
Headers:
Authorization: Bearer <access token>
Content-Type: text/plain
Body:
42/ns,["eventName","arg1","arg2"]
断开连接套接字
POST {endpoint}/api/hubs/{hub}/groups/{group}/:send?api-version=2024-01-01
Headers:
Authorization: Bearer <access token>
Content-Type: text/plain
请求主体
Engine.IO serialized payload for socket disconnection
有关 REST 详细信息,请参阅 “发送到全部 ”。 有关 Engine.IO 协议 的详细信息,请参阅 Engine.IO 协议。 有关断开连接时的有效负载详细信息,请参阅 从命名空间断开连接。
Example
断开myHub集线器中/ns命名空间的socketId套接字连接。
POST {endpoint}/api/hubs/myHub/groups/0~L25z~c29ja2V0SWQ/:send?api-version=2024-01-01
Headers:
Authorization: Bearer <access token>
Content-Type: text/plain
Body:
41/ns,
事件处理程序规范
事件处理程序可以处理来自客户端的connectconnecteddisconnected消息事件,以及其他消息事件。 它们是从服务触发的 REST 调用。
连接事件
服务在套接字连接时触发 connect 事件。 事件处理程序可以使用 connect 事件进行身份验证和接受或拒绝套接字
请求:
POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.com
Content-Type: application/json; charset=utf-8
Content-Length: xxx
ce-specversion: 1.0
ce-type: azure.webpubsub.sys.connect
ce-source: /hubs/{hub}/client/{connectionId}
ce-id: {eventId}
ce-time: 2024-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-connectionId: {connectionId}
ce-hub: {hub}
ce-eventName: connect
ce-namespace: {namespace}
ce-socketId: {socketId}
{
"claims": {}, // claims of jwt of client
"query": {}, // query string of client connect request
"headers": {}, // headers of client connect request
"clientCertificates": [
{
"thumbprint": "ABC"
}
]
}
成功响应:
HTTP/1.1 200 OK
非成功状态代码意味着事件处理程序拒绝该套接字。
连接事件
成功连接套接字时,服务触发 connected 事件。
请求:
POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.com
Content-Type: application/json; charset=utf-8
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.sys.connected
ce-source: /hubs/{hub}/client/{connectionId}
ce-id: {eventId}
ce-time: 2024-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-connectionId: {connectionId}
ce-hub: {hub}
ce-eventName: connected
ce-namespace: {namespace}
ce-socketId: {socketId}
{}
响应:
“Connected”事件是异步的,因此响应并不重要。
HTTP/1.1 200 OK
断开连接事件
服务在套接字断开连接时触发 disconnected 事件。
请求:
POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.com
Content-Type: application/json; charset=utf-8
Content-Length: xxxx
ce-specversion: 1.0
ce-type: azure.webpubsub.sys.disconnected
ce-source: /hubs/{hub}/client/{connectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-connectionId: {connectionId}
ce-hub: {hub}
ce-eventName: disconnected
ce-namespace: {namespace}
ce-socketId: {socketId}
{
"reason": "{Reason}" // Empty if connection close normally. Reason only implies the close is abnormal.
}
响应:
disconnected 是一种异步方法,响应并不重要
HTTP/1.1 200 OK
消息事件
服务会触发具有相同事件名称的相应消息事件。
请求:
POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.com
Content-Type: text/plain
Content-Length: xxxx
ce-specversion: 1.0
ce-type: azure.webpubsub.user.message
ce-source: /hubs/{hub}/client/{connectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-connectionId: {connectionId}
ce-hub: {hub}
ce-eventName: {eventName}
ce-namespace: {namespace}
ce-socketId: {socketId}
Engine.IO serialized payload
响应:
正文中的数据直接发送到相应的客户端。 通常用于确认消息。 (当请求包含 AckId 时)
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: nnnn
UserResponsePayload (Engine.IO serialized payload)
或
HTTP/1.1 204 No Content