Windows 8 引入 ControlChannelTrigger 機制,允許應用程式在進入暫停狀態後,仍能維持 TCP Socket 連線並在背景接收伺服器推送資料。這對於即時通訊、推播通知、遠端監控等長連線場景至關重要。本文深入解析 ControlChannel 的架構、關鍵類別與完整實作流程。
ControlChannel 由三大角色組成:
使用靜態類別 AppContext 在前景應用與背景任務間共享 StreamSocket 與訊息佇列:
public class AppContext {
public static ConcurrentQueue<string> MessageQueue = new ConcurrentQueue<string>();
public static StreamSocket ClientSocket;
}
SocketControlChannel.CreateChannel() 方法完整示範:
public async Task<string> CreateChannel() {
Dispose();
try {
// 申請硬體槽位,心跳間隔 15 分鐘(最小值)
Channel = new ControlChannelTrigger(_channelId, _serverKeepAliveInterval,
ControlChannelTriggerResourceType.RequestHardwareSlot);
} catch (Exception ex) {
Dispose();
return "控制通道建立失敗:" + ex.ToString();
}
// 註冊 KeepAlive 背景任務(需在 Package.appxmanifest 宣告)
var keepAliveBuilder = new BackgroundTaskBuilder();
keepAliveBuilder.Name = "myControlChannelKeepAlive";
keepAliveBuilder.TaskEntryPoint = "BackgroundTaskLib.ControlChannelKeepAlive";
keepAliveBuilder.SetTrigger(Channel.KeepAliveTrigger);
keepAliveBuilder.Register();
// 註冊 Push Notification 背景任務
var pushBuilder = new BackgroundTaskBuilder();
pushBuilder.Name = "myControlChannelPush";
pushBuilder.TaskEntryPoint = "BackgroundTaskLib.ControlChannelPush";
pushBuilder.SetTrigger(Channel.PushNotificationTrigger);
pushBuilder.Register();
// 建立 TCP 連線
_socket = new StreamSocket();
await _socket.ConnectAsync(new HostName("server.example.com"), "8080");
AppContext.ClientSocket = _socket;
_dataWriter = new DataWriter(_socket.OutputStream);
_dataReader = new DataReader(_socket.InputStream);
// 開始非同步接收迴圈
ReceiveLoop();
return "ControlChannel 建立成功";
}
private async void ReceiveLoop() {
try {
while (true) {
uint size = await _dataReader.LoadAsync(4); // 先讀長度前綴
if (size < 4) break;
int len = _dataReader.ReadInt32();
uint loaded = await _dataReader.LoadAsync((uint)len);
if (loaded < len) break;
string msg = _dataReader.ReadString(len);
AppContext.MessageQueue.Enqueue(msg);
}
} catch (Exception ex) {
// 錯誤處理
}
}
在獨立的 Background Task 專案中實作 IBackgroundTask:
public sealed class ControlChannelKeepAlive : IBackgroundTask {
public void Run(IBackgroundTaskInstance taskInstance) {
var deferral = taskInstance.GetDeferral();
var channel = taskInstance.TriggerDetails as ControlChannelTrigger;
if (channel != null && AppContext.ClientSocket != null) {
try {
// 發送心跳封包
var writer = new DataWriter(AppContext.ClientSocket.OutputStream);
writer.WriteInt32(0); // 空心跳
await writer.StoreAsync();
} catch { /* 忽略 */ }
}
deferral.Complete();
}
}
public sealed class ControlChannelPush : IBackgroundTask {
public void Run(IBackgroundTaskInstance taskInstance) {
var deferral = taskInstance.GetDeferral();
var details = taskInstance.TriggerDetails as ControlChannelTriggerEventDetails;
if (details != null && AppContext.ClientSocket != null) {
// 讀取資料並加入佇列,前景應用恢復時可取出處理
// 此處略...
}
deferral.Complete();
}
}
Package.appxmanifest 的「宣告」頁籤加入「背景工作」,類型選「控制通道」,並正確填寫 EntryPoint。0x8007273D 例外。RequestHardwareSlot 較可靠但槽位有限;RequestSoftwareSlot 無限制但可能被系統回收。Windows.Networking.Sockets.WebSocketKeepAlive,無需自行實作。ControlChannel 為 Windows 8 Store Apps 提供了真正的後台長連線能力。雖然設定流程較繁瑣(Manifest、背景任務專案、觸發器註冊),但一旦建立穩定,即能實現低延遲、低功耗的即時資料傳輸。建議封裝成可重用的 SocketControlChannel 元件,並妥善處理例外、重連與生命週期管理。