上一篇介紹了前端 JavaScript 的 WebSocket,
今天來介紹 .Net Core Web API 的 WebSocket。
首先要到 Startup.cs > Configure 去設定一個 WebSocket 的路徑,
然後設定使用 WebSocket 及接受連線的方法。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
// 設定 WebSocket 的路徑
app.Map("/websocket", con =>
{
// 使用 WebSocket
con.UseWebSockets();
// 接受連線的方法
con.Use(async (context, _) =>
{
var socket = await context.WebSockets.AcceptWebSocketAsync();
while (socket.State == WebSocketState.Open)
{
// 因為我只需要後端傳到前端
// 所以就不處理前端傳來的訊息了 XD
await socket.ReceiveAsync(
new ArraySegment<byte>(new byte[1]), CancellationToken.None);
}
});
});
// ...
}
傳送訊息至前端就需要用到前面 AcceptWebSocketAsync 回傳的實例。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
app.Map("/websocket", con =>
{
con.UseWebSockets();
con.Use(async (context, _) =>
{
var socket = await context.WebSockets.AcceptWebSocketAsync();
// 傳送連線成功訊息
var message = "連線成功囉~";
// 傳送的訊息參數類型是 bytes 要轉一下
var messageBuffer = Encoding.UTF8.GetBytes(message);
await socket.SendAsync(
buffer, WebSocketMessageType.Text, true, CancellationToken.None);
while (socket.State == WebSocketState.Open)
{
await socket.ReceiveAsync(
new ArraySegment<byte>(new byte[1]), CancellationToken.None);
}
});
});
// ...
}
理論上,
我們不會只在接收連線時傳送訊息,
也不會只傳訊息給一個連線。
相反的,
我們應該在接受連線時,將其放入一個集合。
並在需要發送訊息時,
使用這個集合來以廣播的方式發送訊息給所有連線。
所以這裡我會傾向用一個 Service 來處理連線。
public class WebsocketHandler
{
// 存放連線的集合
private List<WebSocket> _sockets = new List<WebSocket>();
// 接受連線的方式
public async Task Connect(HttpContext context, Func<Task> _)
{
var socket = await context.WebSockets.AcceptWebSocketAsync();
_sockets.Add(socket); // 將連線加入集合
while (socket.State == WebSocketState.Open)
{
await socket.ReceiveAsync(
new ArraySegment<byte>(new byte[1]), CancellationToken.None);
}
}
// 發送訊息
public async Task SendAsync(object data, CancellationToken ct = default)
{
// 過濾沒有開啟的連線
_sockets =
_sockets.Where(socket => socket.State == WebSocketState.Open).ToList();
// 逐一發送訊息
foreach (WebSocket socket in _sockets)
{
await SendAsync(socket, data, ct);
}
}
private async Task SendAsync(WebSocket socket, object data, CancellationToken ct)
{
// 把資料轉成 JSON string 再轉成 bytes
var dataStr = JObject.FromObject(data).ToString();
var buffer = Encoding.UTF8.GetBytes(dataStr);
// 發送訊息
await socket.SendAsync(buffer, WebSocketMessageType.Text, true, ct);
}
}
然後調整 Startup.cs 的設定
public void ConfigureServices(IServiceCollection services)
{
// ...
// 記得要用 Singleton 連線集合才不會出錯
services.AddSingleton<WebsocketHandler, WebsocketHandler>();
// ...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ...
app.Map("/websocket", con =>
{
con.UseWebSockets();
con.Use(app.ApplicationServices.GetService<WebsocketHandler>().Connect);
});
// ...
}
在發送訊息的部分,我會希望將訊息的格式統一成:
{
"type": "訊息類型",
"data": "訊息資料"
}
方便前端區分資料類型。
所以這邊我會建立一個 Abstract class ,
讓所有需要發送的通知的部分來繼承。
public abstract class AbstractWebsocket
{
private readonly WebsocketHandler _handler;
private readonly WebsocketType _type;
// 除了注入剛剛的 handler 來處理發送訊息的部分
// 還要傳入一個參數來定義資料的類型
protected AbstractWebsocket(WebsocketHandler handler, WebsocketType type)
{
_handler = handler;
_type = type;
}
protected async Task SendAsync(object data)
{
// 傳送的訊息為類型加上資料
await _handler.SendAsync(new { type = _type, data });
}
}
// 繼承範例
public class ExampleWebsocket : AbstractWebsocket, IExampleWebsocket
{
public ExampleWebsocket(WebsocketHandler handler) // 注入 handler
: base(handler, WebsocketType.Example) // 設定類型
{
}
public async Task SendAsync(ExampleModel data) // 這裡可以使用想要的類別
{
await base.SendAsync(data); // 使用抽象類別的發送方法
}
}
在 Startup.cs > ConfigureServices 加入 IExampleWebsocket :
public void ConfigureServices(IServiceCollection services)
{
// ...
// 這邊就可以用 Scoped 或是 Transient
services.AddTransient<IExampleWebsocket, ExampleWebsocket>();
// ...
}
要使用的時候就直接注入 IExampleWebsocket 來使用:
public class ExampleService {
private readonly IExampleWebsocket _websocket;
public ExampleService(IExampleWebsocket websocket) {
_websocket = websocket;
}
public async Task SendNewExample() {
await _websocket.SendAsync(new ExampleModel());
}
}
以上就是我在 .Net Core Web Api 中使用 Websocket 的方式。
如果有更好的方式,一樣歡迎留言推薦給我噢!
When you subscribe to the blog, we will send you an e-mail when there are new updates on the site so you wouldn't miss them.
評論