利用 `Application` 儲存全域共享資料 (線上名單、聊天記錄、人數),`Session` 儲存個別使用者暱稱,配合 `Global.asax` 事件維護線上狀態,快速建立無 SignalR 依賴的傳統聊天室。
public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
Application["OnlineUsers"] = new Dictionary();
Application["ChatLog"] = new StringBuilder();
Application["UserCount"] = 0;
}
protected void Session_Start(object sender, EventArgs e)
{
Application.Lock();
Application["UserCount"] = (int)Application["UserCount"] + 1;
Application.UnLock();
}
protected void Session_End(object sender, EventArgs e)
{
string sessionId = Session.SessionID;
Application.Lock();
var users = (Dictionary)Application["OnlineUsers"];
if (users.ContainsKey(sessionId)) users.Remove(sessionId);
Application["UserCount"] = (int)Application["UserCount"] - 1;
Application.UnLock();
}
}
protected void BtnEnter_Click(object sender, EventArgs e)
{
string nick = txtNick.Text.Trim();
if (string.IsNullOrEmpty(nick))
{
lblErr.Text = "請輸入暱稱";
return;
}
Session["NickName"] = nick;
Response.Redirect("ChatRoom.aspx");
}
protected void Page_Load(object sender, EventArgs e)
{
if (Session["NickName"] == null) { Response.Redirect("Login.aspx"); return; }
lblCount.Text = Application["UserCount"].ToString();
if (!IsPostBack)
{
// 首次載入:將自己加入線上名單
var users = (Dictionary)Application["OnlineUsers"];
Application.Lock();
users[Session.SessionID] = Session["NickName"].ToString();
Application.UnLock();
txtLog.Text = ((StringBuilder)Application["ChatLog"]).ToString();
}
// 每次載入重新綁定名單 (含其他人進出)
lstUsers.DataSource = ((Dictionary)Application["OnlineUsers"]).Values;
lstUsers.DataBind();
}
protected void BtnSend_Click(object sender, EventArgs e)
{
string msg = txtMsg.Text.Trim();
if (string.IsNullOrEmpty(msg)) return;
string nick = Session["NickName"].ToString();
var log = (StringBuilder)Application["ChatLog"];
Application.Lock();
log.AppendLine($"{DateTime.Now:HH:mm:ss} {nick}: {msg}");
Application.UnLock();
txtLog.Text = log.ToString();
txtMsg.Text = "";
// 維持捲軸在底部需客戶端 JS 配合
}
protected void BtnLeave_Click(object sender, EventArgs e)
{
var users = (Dictionary)Application["OnlineUsers"];
Application.Lock();
users.Remove(Session.SessionID);
Application["UserCount"] = (int)Application["UserCount"] - 1;
Application.UnLock();
Session.Abandon();
Response.Redirect("Login.aspx");
}
| 項目 | 說明 |
|---|---|
| Application.Lock/UnLock | 多執行緒環境下保護共享資料,防止競爭條件 |
| StringBuilder 非執行緒安全 | 鎖定期間操作,或改用 ConcurrentQueue + 定期刷新 |
| Session_End 觸發條件 | 僅 InProc 模式生效;StateServer/SQLServer 不觸發 |
| 即時更新 | 本範例需手動重整或設定 ``;生產環境建議 SignalR |
| 擴充性 | 單機限制;Web Farm 需改用 Redis/資料庫共享狀態 |