Windows 8 Store Apps学习(68) 后台任务:控制通道(ControlChannel)2014-09-02 cnblogs webabcd介绍重新想象 Windows 8 Store Apps 之 后台任务控制通道(ControlChannel)示例1、客户端与服务端做 ControlChannel 通信的关键代码ControlChannelHelper/AppContext.cs
/* * 本例通过全局静态变量来实现 app 与 task 的信息共享,以便后台任务可以获取到 app 中的相关信息 ** 注: * 也可以通过 Windows.ApplicationModel.Core.CoreApplication.Properties 保存数据,以实现 app 与 task 的信息共享 */using System.Collections.Concurrent;using Windows.Networking.Sockets;namespace ControlChannelHelper{public class AppContext{/// <summary>/// 从 ControlChannel 接收到的数据/// </summary>public static ConcurrentQueue<string> MessageQueue = new ConcurrentQueue<string>();/// <summary>/// 客户端 socket/// </summary>public static StreamSocket ClientSocket;}}
ControlChannelHelper/SocketControlChannel.cs
/* * 实现一个 socket tcp 通信的 ControlChannel,client 将在此 ControlChannel 中实时接收数据 ** 注: * win8 client 和 socket server 不能部署在同一台机器上,否则会抛出异常:{参考的对象类型不支持尝试的操作。 (异常来自 HRESULT:0x8007273D)} */using System;using System.Threading.Tasks;using Windows.ApplicationModel.Background;using Windows.Foundation;using Windows.Networking;using Windows.Networking.Sockets;using Windows.Storage.Streams;namespace ControlChannelHelper{public class SocketControlChannel : IDisposable{// ControlChannelpublic ControlChannelTrigger Channel { get; set; }// 客户端 socketprivate StreamSocket _socket;// 用于发送数据private DataWriter _dataWriter;// 用于接收数据private DataReader _dataReader;// 向服务端发送心跳的间隔时间,单位为分钟,最小 15 分钟private uint _serverKeepAliveInterval = 15;// ControlChannel 的标识private string _channelId = "myControlChannel";public SocketControlChannel(){}public async Task<string> CreateChannel(){Dispose();try{// 实例化一个 ControlChannelChannel = new ControlChannelTrigger(_channelId, _serverKeepAliveInterval, ControlChannelTriggerResourceType.RequestHardwareSlot);}catch (Exception ex){Dispose();return "控制通道创建失败:" + ex.ToString();}// 注册用于向服务端 socket 发送心跳的后台任务,需要在 manifest 中做相关配置var keepAliveBuilder = new BackgroundTaskBuilder();keepAliveBuilder.Name = "myControlChannelKeepAlive";// 注:如果走的是 WebSocket 协议,则系统已经为其内置了发送心跳的逻辑,此处直接指定为 Windows.Networking.Sockets.WebSocketKeepAlive 即可keepAliveBuilder.TaskEntryPoint = "BackgroundTaskLib.ControlChannelKeepAlive";keepAliveBuilder.SetTrigger(Channel.KeepAliveTrigger); // 到了发送心跳的间隔时间时则触发,本例是 15 分钟keepAliveBuilder.Register();// 注册用于向用户显示通知的后台任务,需要在 manifest 中做相关配置// var pushNotifyBuilder = new BackgroundTaskBuilder();pushNotifyBuilder.Name = "myControlChannelPushNotification";pushNotifyBuilder.TaskEntryPoint = "BackgroundTaskLib.ControlChannelPushNotification";pushNotifyBuilder.SetTrigger(Channel.PushNotificationTrigger); // 在 ControlChannel 中收到了推送过来的数据时则触发pushNotifyBuilder.Register();try{_socket = new StreamSocket();AppContext.ClientSocket = _socket;// 在 ControlChannel 中通过指定的 StreamSocket 通信Channel.UsingTransport(_socket);// client socket 连接 server socketawait _socket.ConnectAsync(new HostName("192.168.6.204"), "3366");// 开始等待 ControlChannel 中推送过来的数据,如果 win8 client 和 socket server 部署在同一台机器上,则此处会抛出异常ControlChannelTriggerStatus status = Channel.WaitForPushEnabled();if (status != ControlChannelTriggerStatus.HardwareSlotAllocated && status != ControlChannelTriggerStatus.SoftwareSlotAllocated)return "控制通道创建失败:" + status.ToString();// 发送数据到服务端_dataWriter = new DataWriter(_socket.OutputStream);string message = "hello " + DateTime.Now.ToString("hh:mm:ss") + "^";_dataWriter.WriteString(message);await _dataWriter.StoreAsync();// 接收数据ReceiveData();}catch (Exception ex){Dispose();return "控制通道创建失败:" + ex.ToString();}return "ok";}// 开始接收此次数据private void ReceiveData(){uint maxBufferLength = 256;try{var buffer = new Windows.Storage.Streams.Buffer(maxBufferLength);var asyncOperation = _socket.InputStream.ReadAsync(buffer, maxBufferLength, InputStreamOptions.Partial);asyncOperation.Completed = (IAsyncOperationWithProgress<IBuffer, uint> asyncInfo, AsyncStatus asyncStatus) =>{switch (asyncStatus){case AsyncStatus.Completed:case AsyncStatus.Error:try{IBuffer bufferRead = asyncInfo.GetResults();uint bytesRead = bufferRead.Length;_dataReader = DataReader.FromBuffer(bufferRead);// 此次数据接收完毕ReceiveCompleted(bytesRead);}catch (Exception ex){AppContext.MessageQueue.Enqueue(ex.ToString());}break;case AsyncStatus.Canceled:AppContext.MessageQueue.Enqueue("接收数据时被取消了");break;}};}catch (Exception ex){AppContext.MessageQueue.Enqueue(ex.ToString());}}public void ReceiveCompleted(uint bytesRead){// 获取此次接收到的数据uint bufferLength = _dataReader.UnconsumedBufferLength;string message = _dataReader.ReadString(bufferLength);// 将接收到的数据放到内存中,由 PushNotificationTrigger 触发的后台任进行处理(当然也可以在此处处理)AppContext.MessageQueue.Enqueue(message);// 开始接收下一次数据ReceiveData();}// 释放资源public void Dispose(){lock (this){if (_dataWriter != null){try{_dataWriter.DetachStream();_dataWriter = null;}catch (Exception ex){}}if (_dataReader != null){try{_dataReader.DetachStream();_dataReader = null;}catch (Exception exp){}}if (_socket != null){_socket.Dispose();_socket = null;}if (Channel != null){Channel.Dispose();Channel = null;}}}}}
2、服务端BackgroundTaskLib/ControlChannelKeepAlive.cs