雖然在 .Net 上面用上了 SSE ( Server-Sent Events ) ,但是手上還是有一堆舊專案( Webfrom )要改
所以就改了一下程式碼測試,我想說應該很順利,交給 GPT 後發現,原來還是有些限制的..
前端(切換使用者+建立 SSE)
我做得很簡單:兩個按鈕,按一下就改 Cookie 的 login_user,然後重建 SSE。
<div>
<button onclick="loginAs('donma')" type="button">切換為 Donma</button>
<button onclick="loginAs('alice')" type="button">切換為 Alice</button>
<script>
let evt = null;
function loginAs(user) {
// 寫入 Cookie
document.cookie = 'login_user=${user}; path=/;';
console.log("切換身分為:" + user);
// 關閉 SSE
if (evt) evt.close();
// 建立新的 SSE
evt = new EventSource("SSEHandler.ashx");
evt.onmessage = function (e) {
console.log("收到伺服器事件:", e.data);
};
}
</script>
</div>
實際專案你一定會換成 JWT、Session、資料庫查詢 但文章的重點在於 證明 WebForm 也能 per-user 推播
後端 SSEHandler,WebForm 不能用 async/await,也不能像 Kestrel 一樣用非同步 IO,所以 SSE 只能靠 Timer 來模擬
<%@ WebHandler Language="C#" Class="SSEHandler" %>
using System;
using System.Web;
public class SSEHandler : IHttpHandler {
public void ProcessRequest(HttpContext context)
{
// 指定 SSE 回應格式
context.Response.ContentType = "text/event-stream; charset=utf-8";
// 關閉輸出緩衝否則資料會卡住,前端會等很久才收到
context.Response.BufferOutput = false;
context.Response.Expires = -1;
// 禁用快取避免延遲
context.Response.CacheControl = "no-cache";
string user = context.Request.Cookies["login_user"]?.Value ?? "訪客";
// 建立 Timer(每秒 Tick 一次)
// 模擬多線呈
var timer = new System.Timers.Timer(1000);
timer.Elapsed += (sender, args) =>
{
try
{
context.Response.Write(
$"data: 您好 {user},這是您專屬的 SSE 訊息:{DateTime.Now}\n\n"
);
context.Response.Flush();
}
catch
{
// SSE 中斷(例如用戶離開、按 F5 等)
timer.Stop();
timer.Dispose();
}
};
timer.AutoReset = true;
timer.Start();
// 阻塞 Request,不讓 Handler 結束
context.ApplicationInstance.CompleteRequest();
System.Threading.Thread.Sleep(-1);
}
public bool IsReusable => false;
}
結論 -
雖然實作結果看起來跟之前文章很像,但是請注意,GPT 告訴我說 因為架構上的差異
請不要!請不要!使用在高併發的專案,畢竟這會造成大量 thread 產生,這時候不如改用 AJAX 或是其他技術來做到
這邊是要特別注意的,不過我手上專案都還好還是就繼續用吧..