事件
Power BI DataViz World Championships
2月14日 下午4時 - 3月31日 下午4時
4 次參賽機會,有機會贏得會議套裝行程,現場參與在拉斯維加斯舉行的總決賽
進一步了解備註
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 如需目前的版本,請參閱 本文的 .NET 9 版本。
本文說明如何在 ASP.NET Core 中開始使用 WebSocket。 WebSocket (RFC 6455) 為通訊協定,其可在 TCP 連線下啟用雙向的持續性通訊通道。 它用於受益於快速且即時通訊的應用程式,例如聊天、儀表板和遊戲應用程式。
檢視或下載範例程式碼 (如何下載、如何執行)。
透過 HTTP/2 使用 WebSockets 會利用新功能,例如:
在所有啟用 HTTP/2 之平台的 Kestrel 中,都提供這些支援的功能。 在瀏覽器和 Kestrel 中,會自動進行版本交涉,因此不需要新的 API。
.NET 7 引入了對 Kestrel 的 WebSockets over HTTP/2 支援、SignalR JavaScript 用戶端和 SignalR 與 Blazor WebAssembly。
備註
HTTP/2 WebSockets 使用 CONNECT 要求,而不是 GET,因此可能需要更新您自己的路由和控制器。 如需詳細資訊,請參閱本文中的新增現有控制器的 HTTP/2 WebSockets 支援。
Chrome 和 Edge 預設會啟用 HTTP/2 WebSockets,而且您可以使用 about:config
旗標以在 FireFox 的 network.http.spdy.websockets
頁面上將其啟用。
WebSockets 原本是針對 HTTP/1.1 所設計,但此後已改編為透過 HTTP/2 運作。 (RFC 8441)
ASP.NET Core SignalR 是程式庫,可簡化將即時 Web 功能新增至應用程式的程序。 它會盡可能使用 WebSockets。
針對大部分的應用程式,建議使用 SignalR,而不是原始 WebSockets。 SignalR:
支援透過 HTTP/2 的 WebSockets 用於:
針對某些應用程式,.NET 上的 gRPC 提供 WebSockets 的替代方案。
在 Program.cs
中,新增 WebSockets 中介軟體:
app.UseWebSockets();
您可以設定下列設定:
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
app.UseWebSockets(webSocketOptions);
在要求生命週期的後半部某處 (例如,在 Program.cs
或動作方法的後半部),檢查其是否為 WebSocket 要求,並接受 WebSocket 要求。
下列範例取自 Program.cs
的後半部:
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
else
{
await next(context);
}
});
WebSocket 要求可以傳入任何 URL,但此範例程式碼只接受 /ws
的要求。
在控制器方法中,可以採用類似的方式:
public class WebSocketController : ControllerBase
{
[Route("/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
使用 WebSocket 時,您必須確保中介軟體管線會在連線期間內持續執行。 如果您嘗試在中介軟體管線結束後傳送或接收 WebSocket 訊息,便可能會收到類似下列的例外狀況:
System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake. ---> System.ObjectDisposedException: Cannot write to the response body, the response has completed.
Object name: 'HttpResponseStream'.
如果您是使用背景服務來將資料寫入 WebSocket,請務必使中介軟體管線持續執行。 請使用 TaskCompletionSource<TResult> 來這麼做。 將 TaskCompletionSource
傳遞至您的背景服務,並讓它在您完成使用 WebSocket 時呼叫 TrySetResult。 然後,在要求期間,await
Task 屬性,如下列範例所示:
app.Run(async (context) =>
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
});
太快從動作方法返回時,也可能會發生關閉 WebSocket 例外狀況。 在動作方法中接受通訊端時,請先等候可使用通訊端的程式碼完成,再從動作方法返回。
絕不要使用 Task.Wait
、Task.Result
或類似的封鎖呼叫等候通訊端完成,因為這會導致嚴重的執行緒處理問題。 一律使用 await
。
.NET 7 引入了對 Kestrel 的 WebSockets over HTTP/2 支援、SignalR JavaScript 用戶端和 SignalR 與 Blazor WebAssembly。 HTTP/2 WebSockets 使用 CONNECT 要求,而不是 GET。 如果您先前已在 Websocket 要求的控制器動作方法上使用 [HttpGet("/path")]
,則請將其更新為改用 [Route("/path")]
。
public class WebSocketController : ControllerBase
{
[Route("/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
警告
透過已加密連線來啟用壓縮,可以讓應用程式受到 CRIME/BREACH 攻擊。
如果傳送敏感性資訊,則請避免在呼叫 WebSocketMessageFlags.DisableCompression
時啟用壓縮或使用 WebSocket.SendAsync
。
這適用於 WebSocket 的兩端。 請注意,瀏覽器中的 WebSockets API 沒有針對每個傳送停用壓縮的設定。
如果需要透過 WebSockets 壓縮訊息,則接受程式碼必須指定其允許壓縮,如下所示:
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
new WebSocketAcceptContext { DangerousEnableCompression = true }))
{
}
WebSocketAcceptContext.ServerMaxWindowBits
和 WebSocketAcceptContext.DisableServerContextTakeover
是可控制壓縮運作方式的進階選項。
第一次建立連線時,會在用戶端與伺服器之間交涉壓縮。 您可以在 WebSocket RFC 的壓縮延伸模組中深入閱讀交涉。
備註
如果伺服器或用戶端未接受壓縮交涉,則仍然會建立連線。 不過,連線不會在傳送和接收訊息時使用壓縮。
AcceptWebSocketAsync
方法可將 TCP 連線升級為 WebSocket 連線,並提供 WebSocket 物件。 請使用 WebSocket
物件來傳送和接收訊息。
稍早所示接受 WebSocket 要求的程式碼會將 WebSocket
物件傳遞給 Echo
方法。 程式碼會收到一則訊息,並立即傳送回相同的訊息。 在用戶端關閉連線之前,訊息會在迴圈中傳送和接收:
private static async Task Echo(WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
var receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
while (!receiveResult.CloseStatus.HasValue)
{
await webSocket.SendAsync(
new ArraySegment<byte>(buffer, 0, receiveResult.Count),
receiveResult.MessageType,
receiveResult.EndOfMessage,
CancellationToken.None);
receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync(
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None);
}
如果在開始此迴圈之前接受 WebSocket 連線,中介軟體管線就會結束。 在關閉通訊端後,管線會回溯。 也就是說,在接受 WebSocket 後,要求就會在管線中停止向前移動。 當迴圈完成且通訊端關閉時,要求會繼續備份管線。
用戶端因連線中斷而中斷連線時,不會自動通知伺服器。 只有當用戶端傳送通知時,伺服器才會收到連線中斷訊息,而在網際網路連線中斷的情況下無法傳送通知。 若想要在發生此情況時採取一些動作,請設定逾時,在指定的時間內未從用戶端收到通知之後觸發逾時。
如果用戶端並非總是會傳送訊息,而且您不想讓系統只是因為連線閒置就觸發逾時,則請讓用戶端使用計時器每隔 X 秒傳送一次 Ping 訊息。 在伺服器上,如果訊息未在收到前一個訊息後的 2*X 秒內抵達,則終止連線,並回報用戶端已中斷連線。 等候預期時間間隔兩倍的時間,為網路延遲保留足夠的額外時間,看看能不能收到耽誤的 Ping 訊息。
CORS 所提供的保護不套用至 WebSocket。 瀏覽器不會:
Access-Control
標頭中所指定的限制。不過,瀏覽器會在發出 WebSocket 要求時,傳送 Origin
標頭。 應設定應用程式驗證這些標頭,以確保只允許來自預期來源的 WebSocket。
如果您在 "https://server.com"" 上裝載伺服器,並在 "https://client.com"" 上裝載用戶端,則請將 "https://client.com"" 新增至 AllowedOrigins 清單,以讓 WebSockets 進行驗證。
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");
app.UseWebSockets(webSocketOptions);
備註
因為 Origin
由用戶端控制,所以和 Referer
標頭一樣可能受到偽造。
請勿使用這些標頭作為驗證機制。
搭配 IIS/IIS Express 8 或更新版本的 Windows Server 2012 或更新版本和 Windows 8 或更新版本支援 WebSocket 通訊協定,但不支援透過 HTTP/2 的 WebSockets。
備註
使用 IIS Express 時一律會啟用 WebSockets。
若要在 Windows Server 2012 或更新版本中啟用 WebSocket 通訊協定的支援:
備註
使用 IIS Express 時,不需要這些步驟
若要在 Windows 8 或更新版本中啟用 WebSocket 通訊協定的支援:
備註
使用 IIS Express 時,不需要這些步驟
如果在 Node.js上的 socket.io 中使用 WebSocket 支援,請使用 web.config 或 applicationHost.config中的 webSocket
元素停用預設 IIS WebSocket 模組。如果未執行此步驟,IIS WebSocket 模組會嘗試處理 WebSocket 通訊,而不是 Node.js 和應用程式。
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
本文附帶的範例應用程式是回應應用程式。 其具有一個進行 WebSocket 連線的網頁,而伺服器會將其所接收的任何訊息都重新傳送回用戶端。 使用 .NET 7 或更新版本的目標架構時,此範例應用程式支援透過 HTTP/2 的 WebSockets。
執行應用程式:
dotnet run
,並在瀏覽器中導覽至 http://localhost:<port>
。網頁會顯示連線狀態:
選取 [連線] 將 WebSocket 要求傳送到顯示的 URL。 輸入測試訊息,然後選取 [傳送]。 完成後,請選取 [關閉通訊端]。 [通訊記錄檔] 區段會報告每次進行的開啟、傳送和關閉動作。
本文說明如何在 ASP.NET Core 中開始使用 WebSocket。 WebSocket (RFC 6455) 為通訊協定,其可在 TCP 連線下啟用雙向的持續性通訊通道。 它用於受益於快速且即時通訊的應用程式,例如聊天、儀表板和遊戲應用程式。
檢視或下載範例程式碼 (如何下載、如何執行)。
透過 HTTP/2 使用 WebSockets 會利用新功能,例如:
在所有啟用 HTTP/2 之平台的 Kestrel 中,都提供這些支援的功能。 在瀏覽器和 Kestrel 中,會自動進行版本交涉,因此不需要新的 API。
.NET 7 引入了對 Kestrel 的 WebSockets over HTTP/2 支援、SignalR JavaScript 用戶端和 SignalR 與 Blazor WebAssembly。
備註
HTTP/2 WebSockets 使用 CONNECT 要求,而不是 GET,因此可能需要更新您自己的路由和控制器。 如需詳細資訊,請參閱本文中的新增現有控制器的 HTTP/2 WebSockets 支援。
Chrome 和 Edge 預設會啟用 HTTP/2 WebSockets,而且您可以使用 about:config
旗標以在 FireFox 的 network.http.spdy.websockets
頁面上將其啟用。
WebSockets 原本是針對 HTTP/1.1 所設計,但此後已改編為透過 HTTP/2 運作。 (RFC 8441)
ASP.NET Core SignalR 是程式庫,可簡化將即時 Web 功能新增至應用程式的程序。 它會盡可能使用 WebSockets。
針對大部分的應用程式,建議使用 SignalR,而不是原始 WebSockets。 SignalR:
支援透過 HTTP/2 的 WebSockets 用於:
針對某些應用程式,.NET 上的 gRPC 提供 WebSockets 的替代方案。
在 Program.cs
中,新增 WebSockets 中介軟體:
app.UseWebSockets();
您可以設定下列設定:
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
app.UseWebSockets(webSocketOptions);
在要求生命週期的後半部某處 (例如,在 Program.cs
或動作方法的後半部),檢查其是否為 WebSocket 要求,並接受 WebSocket 要求。
下列範例取自 Program.cs
的後半部:
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
else
{
await next(context);
}
});
WebSocket 要求可以傳入任何 URL,但此範例程式碼只接受 /ws
的要求。
在控制器方法中,可以採用類似的方式:
public class WebSocketController : ControllerBase
{
[Route("/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
使用 WebSocket 時,您必須確保中介軟體管線會在連線期間內持續執行。 如果您嘗試在中介軟體管線結束後傳送或接收 WebSocket 訊息,便可能會收到類似下列的例外狀況:
System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake. ---> System.ObjectDisposedException: Cannot write to the response body, the response has completed.
Object name: 'HttpResponseStream'.
如果您是使用背景服務來將資料寫入 WebSocket,請務必使中介軟體管線持續執行。 請使用 TaskCompletionSource<TResult> 來這麼做。 將 TaskCompletionSource
傳遞至您的背景服務,並讓它在您完成使用 WebSocket 時呼叫 TrySetResult。 然後,在要求期間,await
Task 屬性,如下列範例所示:
app.Run(async (context) =>
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
});
太快從動作方法返回時,也可能會發生關閉 WebSocket 例外狀況。 在動作方法中接受通訊端時,請先等候可使用通訊端的程式碼完成,再從動作方法返回。
絕不要使用 Task.Wait
、Task.Result
或類似的封鎖呼叫等候通訊端完成,因為這會導致嚴重的執行緒處理問題。 一律使用 await
。
.NET 7 引入了對 Kestrel 的 WebSockets over HTTP/2 支援、SignalR JavaScript 用戶端和 SignalR 與 Blazor WebAssembly。 HTTP/2 WebSockets 使用 CONNECT 要求,而不是 GET。 如果您先前已在 Websocket 要求的控制器動作方法上使用 [HttpGet("/path")]
,則請將其更新為改用 [Route("/path")]
。
public class WebSocketController : ControllerBase
{
[Route("/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
警告
透過已加密連線來啟用壓縮,可以讓應用程式受到 CRIME/BREACH 攻擊。
如果傳送敏感性資訊,則請避免在呼叫 WebSocketMessageFlags.DisableCompression
時啟用壓縮或使用 WebSocket.SendAsync
。
這適用於 WebSocket 的兩端。 請注意,瀏覽器中的 WebSockets API 沒有針對每個傳送停用壓縮的設定。
如果需要透過 WebSockets 壓縮訊息,則接受程式碼必須指定其允許壓縮,如下所示:
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
new WebSocketAcceptContext { DangerousEnableCompression = true }))
{
}
WebSocketAcceptContext.ServerMaxWindowBits
和 WebSocketAcceptContext.DisableServerContextTakeover
是可控制壓縮運作方式的進階選項。
第一次建立連線時,會在用戶端與伺服器之間交涉壓縮。 您可以在 WebSocket RFC 的壓縮延伸模組中深入閱讀交涉。
備註
如果伺服器或用戶端未接受壓縮交涉,則仍然會建立連線。 不過,連線不會在傳送和接收訊息時使用壓縮。
AcceptWebSocketAsync
方法可將 TCP 連線升級為 WebSocket 連線,並提供 WebSocket 物件。 請使用 WebSocket
物件來傳送和接收訊息。
稍早所示接受 WebSocket 要求的程式碼會將 WebSocket
物件傳遞給 Echo
方法。 程式碼會收到一則訊息,並立即傳送回相同的訊息。 在用戶端關閉連線之前,訊息會在迴圈中傳送和接收:
private static async Task Echo(WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
var receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
while (!receiveResult.CloseStatus.HasValue)
{
await webSocket.SendAsync(
new ArraySegment<byte>(buffer, 0, receiveResult.Count),
receiveResult.MessageType,
receiveResult.EndOfMessage,
CancellationToken.None);
receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync(
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None);
}
如果在開始此迴圈之前接受 WebSocket 連線,中介軟體管線就會結束。 在關閉通訊端後,管線會回溯。 也就是說,在接受 WebSocket 後,要求就會在管線中停止向前移動。 當迴圈完成且通訊端關閉時,要求會繼續備份管線。
用戶端因連線中斷而中斷連線時,不會自動通知伺服器。 只有當用戶端傳送通知時,伺服器才會收到連線中斷訊息,而在網際網路連線中斷的情況下無法傳送通知。 若想要在發生此情況時採取一些動作,請設定逾時,在指定的時間內未從用戶端收到通知之後觸發逾時。
如果用戶端並非總是會傳送訊息,而且您不想讓系統只是因為連線閒置就觸發逾時,則請讓用戶端使用計時器每隔 X 秒傳送一次 Ping 訊息。 在伺服器上,如果訊息未在收到前一個訊息後的 2*X 秒內抵達,則終止連線,並回報用戶端已中斷連線。 等候預期時間間隔兩倍的時間,為網路延遲保留足夠的額外時間,看看能不能收到耽誤的 Ping 訊息。
CORS 所提供的保護不套用至 WebSocket。 瀏覽器不會:
Access-Control
標頭中所指定的限制。不過,瀏覽器會在發出 WebSocket 要求時,傳送 Origin
標頭。 應設定應用程式驗證這些標頭,以確保只允許來自預期來源的 WebSocket。
如果您在 "https://server.com"" 上裝載伺服器,並在 "https://client.com"" 上裝載用戶端,則請將 "https://client.com"" 新增至 AllowedOrigins 清單,以讓 WebSockets 進行驗證。
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");
app.UseWebSockets(webSocketOptions);
備註
因為 Origin
由用戶端控制,所以和 Referer
標頭一樣可能受到偽造。
請勿使用這些標頭作為驗證機制。
搭配 IIS/IIS Express 8 或更新版本的 Windows Server 2012 或更新版本和 Windows 8 或更新版本支援 WebSocket 通訊協定,但不支援透過 HTTP/2 的 WebSockets。
備註
使用 IIS Express 時一律會啟用 WebSockets。
若要在 Windows Server 2012 或更新版本中啟用 WebSocket 通訊協定的支援:
備註
使用 IIS Express 時,不需要這些步驟
若要在 Windows 8 或更新版本中啟用 WebSocket 通訊協定的支援:
備註
使用 IIS Express 時,不需要這些步驟
如果在 Node.js上的 socket.io 中使用 WebSocket 支援,請使用 web.config 或 applicationHost.config中的 webSocket
元素停用預設 IIS WebSocket 模組。如果未執行此步驟,IIS WebSocket 模組會嘗試處理 WebSocket 通訊,而不是 Node.js 和應用程式。
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
本文附帶的範例應用程式是回應應用程式。 其具有一個進行 WebSocket 連線的網頁,而伺服器會將其所接收的任何訊息都重新傳送回用戶端。 使用 .NET 7 或更新版本的目標架構時,此範例應用程式支援透過 HTTP/2 的 WebSockets。
執行應用程式:
dotnet run
,並在瀏覽器中導覽至 http://localhost:<port>
。網頁會顯示連線狀態:
選取 [連線] 將 WebSocket 要求傳送到顯示的 URL。 輸入測試訊息,然後選取 [傳送]。 完成後,請選取 [關閉通訊端]。 [通訊記錄檔] 區段會報告每次進行的開啟、傳送和關閉動作。
本文說明如何在 ASP.NET Core 中開始使用 WebSocket。 WebSocket (RFC 6455) 為通訊協定,其可在 TCP 連線下啟用雙向的持續性通訊通道。 它用於受益於快速且即時通訊的應用程式,例如聊天、儀表板和遊戲應用程式。
檢視或下載範例程式碼 (如何下載、如何執行)。
ASP.NET Core SignalR 是程式庫,可簡化將即時 Web 功能新增至應用程式的程序。 它會盡可能使用 WebSockets。
針對大部分的應用程式,建議使用透過原始 WebSockets 的 SignalR。 SignalR 針對未提供 WebSockets 的環境,提供傳輸後援。 其也提供基本的遠端程序呼叫應用程式模型。 而且,在大部分的情況下,相較於使用原始 WebSockets,SignalR 沒有顯著的效能缺點。
針對某些應用程式,.NET 上的 gRPC 提供 WebSockets 的替代方案。
在 Program.cs
中,新增 WebSockets 中介軟體:
app.UseWebSockets();
您可以設定下列設定:
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
app.UseWebSockets(webSocketOptions);
在要求生命週期的後半部某處 (例如,在 Program.cs
或動作方法的後半部),檢查其是否為 WebSocket 要求,並接受 WebSocket 要求。
下列範例取自 Program.cs
的後半部:
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
else
{
await next(context);
}
});
WebSocket 要求可以傳入任何 URL,但此範例程式碼只接受 /ws
的要求。
在控制器方法中,可以採用類似的方式:
public class WebSocketController : ControllerBase
{
[HttpGet("/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
使用 WebSocket 時,您必須確保中介軟體管線會在連線期間內持續執行。 如果您嘗試在中介軟體管線結束後傳送或接收 WebSocket 訊息,便可能會收到類似下列的例外狀況:
System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake. ---> System.ObjectDisposedException: Cannot write to the response body, the response has completed.
Object name: 'HttpResponseStream'.
如果您是使用背景服務來將資料寫入 WebSocket,請務必使中介軟體管線持續執行。 請使用 TaskCompletionSource<TResult> 來這麼做。 將 TaskCompletionSource
傳遞至您的背景服務,並讓它在您完成使用 WebSocket 時呼叫 TrySetResult。 然後,在要求期間,await
Task 屬性,如下列範例所示:
app.Run(async (context) =>
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
});
太快從動作方法返回時,也可能會發生關閉 WebSocket 例外狀況。 在動作方法中接受通訊端時,請先等候可使用通訊端的程式碼完成,再從動作方法返回。
絕不要使用 Task.Wait
、Task.Result
或類似的封鎖呼叫等候通訊端完成,因為這會導致嚴重的執行緒處理問題。 一律使用 await
。
警告
透過已加密連線來啟用壓縮,可以讓應用程式受到 CRIME/BREACH 攻擊。
如果傳送敏感性資訊,則請避免在呼叫 WebSocketMessageFlags.DisableCompression
時啟用壓縮或使用 WebSocket.SendAsync
。
這適用於 WebSocket 的兩端。 請注意,瀏覽器中的 WebSockets API 沒有針對每個傳送停用壓縮的設定。
如果需要透過 WebSockets 壓縮訊息,則接受程式碼必須指定其允許壓縮,如下所示:
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
new WebSocketAcceptContext { DangerousEnableCompression = true }))
{
}
WebSocketAcceptContext.ServerMaxWindowBits
和 WebSocketAcceptContext.DisableServerContextTakeover
是可控制壓縮運作方式的進階選項。
第一次建立連線時,會在用戶端與伺服器之間交涉壓縮。 您可以在 WebSocket RFC 的壓縮延伸模組中深入閱讀交涉。
備註
如果伺服器或用戶端未接受壓縮交涉,則仍然會建立連線。 不過,連線不會在傳送和接收訊息時使用壓縮。
AcceptWebSocketAsync
方法可將 TCP 連線升級為 WebSocket 連線,並提供 WebSocket 物件。 請使用 WebSocket
物件來傳送和接收訊息。
稍早所示接受 WebSocket 要求的程式碼會將 WebSocket
物件傳遞給 Echo
方法。 程式碼會收到一則訊息,並立即傳送回相同的訊息。 在用戶端關閉連線之前,訊息會在迴圈中傳送和接收:
private static async Task Echo(WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
var receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
while (!receiveResult.CloseStatus.HasValue)
{
await webSocket.SendAsync(
new ArraySegment<byte>(buffer, 0, receiveResult.Count),
receiveResult.MessageType,
receiveResult.EndOfMessage,
CancellationToken.None);
receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync(
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None);
}
如果在開始此迴圈之前接受 WebSocket 連線,中介軟體管線就會結束。 在關閉通訊端後,管線會回溯。 也就是說,在接受 WebSocket 後,要求就會在管線中停止向前移動。 當迴圈完成且通訊端關閉時,要求會繼續備份管線。
用戶端因連線中斷而中斷連線時,不會自動通知伺服器。 只有當用戶端傳送通知時,伺服器才會收到連線中斷訊息,而在網際網路連線中斷的情況下無法傳送通知。 若想要在發生此情況時採取一些動作,請設定逾時,在指定的時間內未從用戶端收到通知之後觸發逾時。
如果用戶端並非總是會傳送訊息,而且您不想讓系統只是因為連線閒置就觸發逾時,則請讓用戶端使用計時器每隔 X 秒傳送一次 Ping 訊息。 在伺服器上,如果訊息未在收到前一個訊息後的 2*X 秒內抵達,則終止連線,並回報用戶端已中斷連線。 等候預期時間間隔兩倍的時間,為網路延遲保留足夠的額外時間,看看能不能收到耽誤的 Ping 訊息。
CORS 所提供的保護不套用至 WebSocket。 瀏覽器不會:
Access-Control
標頭中所指定的限制。不過,瀏覽器會在發出 WebSocket 要求時,傳送 Origin
標頭。 應設定應用程式驗證這些標頭,以確保只允許來自預期來源的 WebSocket。
如果您在 "https://server.com"" 上裝載伺服器,並在 "https://client.com"" 上裝載用戶端,則請將 "https://client.com"" 新增至 AllowedOrigins 清單,以讓 WebSockets 進行驗證。
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");
app.UseWebSockets(webSocketOptions);
備註
因為 Origin
由用戶端控制,所以和 Referer
標頭一樣可能受到偽造。
請勿使用這些標頭作為驗證機制。
搭配 IIS/IIS Express 8 或更新版本的 Windows Server 2012 或更新版本和 Windows 8 或更新版本具有 WebSocket 通訊協定的支援。
備註
使用 IIS Express 時一律會啟用 WebSockets。
若要在 Windows Server 2012 或更新版本中啟用 WebSocket 通訊協定的支援:
備註
使用 IIS Express 時,不需要這些步驟
若要在 Windows 8 或更新版本中啟用 WebSocket 通訊協定的支援:
備註
使用 IIS Express 時,不需要這些步驟
如果在 Node.js上的 socket.io 中使用 WebSocket 支援,請使用 web.config 或 applicationHost.config中的 webSocket
元素停用預設 IIS WebSocket 模組。如果未執行此步驟,IIS WebSocket 模組會嘗試處理 WebSocket 通訊,而不是 Node.js 和應用程式。
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
本文附帶的範例應用程式是回應應用程式。 其具有一個進行 WebSocket 連線的網頁,而伺服器會將其所接收的任何訊息都重新傳送回用戶端。 範例應用程式未設定為使用 IIS Express 以從 Visual Studio 執行,因此會使用 dotnet run
以在命令殼層中執行應用程式,並在瀏覽器中導覽至 http://localhost:<port>
。 網頁會顯示連線狀態:
選取 [連線] 將 WebSocket 要求傳送到顯示的 URL。 輸入測試訊息,然後選取 [傳送]。 完成後,請選取 [關閉通訊端]。 [通訊記錄檔] 區段會報告每次進行的開啟、傳送和關閉動作。
本文說明如何在 ASP.NET Core 中開始使用 WebSocket。 WebSocket (RFC 6455) 為通訊協定,其可在 TCP 連線下啟用雙向的持續性通訊通道。 它用於受益於快速且即時通訊的應用程式,例如聊天、儀表板和遊戲應用程式。
檢視或下載範例程式碼 (如何下載)。 如何執行。
ASP.NET Core SignalR 是程式庫,可簡化將即時 Web 功能新增至應用程式的程序。 它會盡可能使用 WebSockets。
針對大部分的應用程式,建議使用透過原始 WebSockets 的 SignalR。 SignalR 針對未提供 WebSockets 的環境,提供傳輸後援。 其也提供基本的遠端程序呼叫應用程式模型。 而且,在大部分的情況下,相較於使用原始 WebSockets,SignalR 沒有顯著的效能缺點。
針對某些應用程式,.NET 上的 gRPC 提供 WebSockets 的替代方案。
在 Configure
類別的 Startup
方法中新增 WebSocket 中介軟體:
app.UseWebSockets();
備註
如果您想要接受控制器中的 WebSocket 要求,則必須在 app.UseWebSockets
之前呼叫 app.UseEndpoints
。
您可以設定下列設定:
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
};
app.UseWebSockets(webSocketOptions);
在要求生命週期的後半部某處 (例如,在 Configure
方法或動作方法的後半部),檢查它是否為 WebSocket 要求並接受 WebSocket 要求。
下列範例取自 Configure
方法的後半部:
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
using (WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync())
{
await Echo(context, webSocket);
}
}
else
{
context.Response.StatusCode = (int) HttpStatusCode.BadRequest;
}
}
else
{
await next();
}
});
WebSocket 要求可以傳入任何 URL,但此範例程式碼只接受 /ws
的要求。
在控制器方法中,可以採用類似的方式:
public class WebSocketController : ControllerBase
{
[HttpGet("/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
使用 WebSocket 時,您必須確保中介軟體管線會在連線期間內持續執行。 如果您嘗試在中介軟體管線結束後傳送或接收 WebSocket 訊息,便可能會收到類似下列的例外狀況:
System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake. ---> System.ObjectDisposedException: Cannot write to the response body, the response has completed.
Object name: 'HttpResponseStream'.
如果您是使用背景服務來將資料寫入 WebSocket,請務必使中介軟體管線持續執行。 請使用 TaskCompletionSource<TResult> 來這麼做。 將 TaskCompletionSource
傳遞至您的背景服務,並讓它在您完成使用 WebSocket 時呼叫 TrySetResult。 然後,在要求期間,await
Task 屬性,如下列範例所示:
app.Use(async (context, next) =>
{
using (WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync())
{
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
}
});
太快從動作方法返回時,也可能會發生關閉 WebSocket 例外狀況。 在動作方法中接受通訊端時,請先等候可使用通訊端的程式碼完成,再從動作方法返回。
絕不要使用 Task.Wait
、Task.Result
或類似的封鎖呼叫等候通訊端完成,因為這會導致嚴重的執行緒處理問題。 一律使用 await
。
AcceptWebSocketAsync
方法可將 TCP 連線升級為 WebSocket 連線,並提供 WebSocket 物件。 請使用 WebSocket
物件來傳送和接收訊息。
稍早所示接受 WebSocket 要求的程式碼會將 WebSocket
物件傳遞給 Echo
方法。 程式碼會收到一則訊息,並立即傳送回相同的訊息。 在用戶端關閉連線之前,訊息會在迴圈中傳送和接收:
private async Task Echo(HttpContext context, WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
while (!result.CloseStatus.HasValue)
{
await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}
如果在開始此迴圈之前接受 WebSocket 連線,中介軟體管線就會結束。 在關閉通訊端後,管線會回溯。 也就是說,在接受 WebSocket 後,要求就會在管線中停止向前移動。 當迴圈完成且通訊端關閉時,要求會繼續備份管線。
用戶端因連線中斷而中斷連線時,不會自動通知伺服器。 只有當用戶端傳送通知時,伺服器才會收到連線中斷訊息,而在網際網路連線中斷的情況下無法傳送通知。 若想要在發生此情況時採取一些動作,請設定逾時,在指定的時間內未從用戶端收到通知之後觸發逾時。
如果用戶端並非總是會傳送訊息,而且您不想讓系統只是因為連線閒置就觸發逾時,則請讓用戶端使用計時器每隔 X 秒傳送一次 Ping 訊息。 在伺服器上,如果訊息未在收到前一個訊息後的 2*X 秒內抵達,則終止連線,並回報用戶端已中斷連線。 等候預期時間間隔兩倍的時間,為網路延遲保留足夠的額外時間,看看能不能收到耽誤的 Ping 訊息。
備註
如果 ManagedWebSocket
選項大於零 (預設為 30 秒 (KeepAliveInterval
)),則內部 TimeSpan.FromSeconds(30)
會隱含處理 Ping/Pong 訊框以保持連線處於活動狀態。
CORS 所提供的保護不套用至 WebSocket。 瀏覽器不會:
Access-Control
標頭中所指定的限制。不過,瀏覽器會在發出 WebSocket 要求時,傳送 Origin
標頭。 應設定應用程式驗證這些標頭,以確保只允許來自預期來源的 WebSocket。
如果您在 "https://server.com"" 上裝載伺服器,並在 "https://client.com"" 上裝載用戶端,則請將 "https://client.com"" 新增至 AllowedOrigins 清單,以讓 WebSockets 進行驗證。
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
};
webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");
app.UseWebSockets(webSocketOptions);
備註
因為 Origin
由用戶端控制,所以和 Referer
標頭一樣可能受到偽造。
請勿使用這些標頭作為驗證機制。
搭配 IIS/IIS Express 8 或更新版本的 Windows Server 2012 或更新版本和 Windows 8 或更新版本具有 WebSocket 通訊協定的支援。
備註
使用 IIS Express 時一律會啟用 WebSockets。
若要在 Windows Server 2012 或更新版本中啟用 WebSocket 通訊協定的支援:
備註
使用 IIS Express 時,不需要這些步驟
若要在 Windows 8 或更新版本中啟用 WebSocket 通訊協定的支援:
備註
使用 IIS Express 時,不需要這些步驟
如果在 Node.js上的 socket.io 中使用 WebSocket 支援,請使用 web.config 或 applicationHost.config中的 webSocket
元素停用預設 IIS WebSocket 模組。如果未執行此步驟,IIS WebSocket 模組會嘗試處理 WebSocket 通訊,而不是 Node.js 和應用程式。
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
本文附帶的範例應用程式是回應應用程式。 其具有一個進行 WebSocket 連線的網頁,而伺服器會將其所接收的任何訊息都重新傳送回用戶端。 範例應用程式未設定為使用 IIS Express 以從 Visual Studio 執行,因此會使用 dotnet run
以在命令殼層中執行應用程式,並在瀏覽器中導覽至 http://localhost:5000
。 網頁會顯示連線狀態:
選取 [連線] 將 WebSocket 要求傳送到顯示的 URL。 輸入測試訊息,然後選取 [傳送]。 完成後,請選取 [關閉通訊端]。 [通訊記錄檔] 區段會報告每次進行的開啟、傳送和關閉動作。
事件
Power BI DataViz World Championships
2月14日 下午4時 - 3月31日 下午4時
4 次參賽機會,有機會贏得會議套裝行程,現場參與在拉斯維加斯舉行的總決賽
進一步了解