英特尔® 通用连接框架(英特尔® CCF)是一款面向移动设备上运行的应用的连接软件。 使用英特尔 CCF 的应用能够将所有用户连接在一起,无论其在世界的另一端使用者不同的防火墙,还是同处一室但没有连接互联网。 英特尔 CFF 适用于 iOS*、Android*、Windows* Desktop 和 Windows 商店的所有应用,这些应用可在任意平台上使用任何外形的英特尔 CCF。 使用英特尔 CCF,开发人员能够开发在手机、平板电脑、PC 和其他智能设备上使用的应用。
英特尔 CFF 的通信模型是对等的方式。 它支持人们直接与彼此连接,并分享其所有移动计算设备间的信息。
本文中,我将介绍如何使用英特尔 CCF 3.0 为 Windows 8 设备开发应用。 我曾负责开发能够在 Windows 8 和 Android 设备之间传输文件的应用的项目,在该项目中,我独立开发了一款 Windows 应用商店应用。 在这里,我将与大家分享使用英特尔 CCF 的经验。
首先,你需要在 Microsoft Visual Studio* 中将 lib 文件与该项目相关联。 英特尔 CCF SDK 包含两个 dll 文件:libMetroSTC 和 WinRTSTC,所有英特尔 CFF 应用都需要这两个文件。 如要使用英特尔 CCF API,需要将 Intel.STC.winmd 添加至项目参考。 winmd 文件包含使用英特尔 CFF SDK 构建 Windows 应用商店应用的元数据。
身份设置
在将会话显示前,英特尔 CCF 用户必须先设置身份,包括用户名、设备名和 avatar。 这是远程用户将看到的身份。 在面向 Windows 应用商店应用的 SDK 中,InviteAssist 类支持用户设置英特尔 CCF 身份。
string displayName = await UserInformation.GetDisplayNameAsync(); _inviteAssist = InviteAssist.GetInstance(); _inviteAssist.SetUserName(displayName); _inviteAssist.SetStatusText("Status"); _inviteAssist.SetSessionName("Win 8"); if (_AvatarStorageFile == null) { Windows.Storage.StorageFile imgfile = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Device.png")); _AvatarStorageFile = imgfile; } await _inviteAssist.SetAvatarAsync(_AvatarStorageFile);
实施 SetUserProfile() 函数获取 InviteAssist 类的实例,通过调用 InviteAssist.SetUserName()、InviteAssist.SetSessionName() 和 InviteAssist.SetAvatarAsync() API 设置配置文件,如下所示。 我将用户名设置为 Windows 账户名,我的 Windows 8 设备将连接的所有设备上都能看到该指派。 状态文本和会话名可由用户在 UI 中定义。 在我的案例中,这些参数不能被用户更改,且一直使用我的符号。 现在,配置文件已设置完成并可由远程用户发现。
发现
英特尔 CCF 远程用户发现可通过调用 Intel.STC.Api.STCSession.GetNeighborhood() API 来完成,它能够返回发现的所有远程 STCSessions 的 IObservableVector<object>。 开发人员需要负责在 observable collection 与 UI 之间实施数据绑定(Data Bind),或从背后的代码更新 UI 以显示用户列表。 我使用了 Grid APP (XAML),它是 Visual Studio 中渲染 GUI 的标准模板。 GridList 结果中显示的所有用户。
为 NeighborhoodUsers 类对象创建一个 ObservableCollection。 _userList 是 STCSession 类对象的列表,并包括周围用户(neighborhood user)列表。
private static List<STCSession> _userList; IObservableVector<object> _hood; ObservableCollection<NeighborhoodUsers> _neighborhoodList = new ObservableCollection<NeighborhoodUsers>(); ObservableCollection<NeighborhoodUsers> neighborhoodList { get { return _neighborhoodList; } }通过调用 STCSession.GetNeighborhood()API 获取 IObservableVector,并为 IObservableVector 设置 VectorChanged 事件处理程序。
async void GetNeighborhoodList() { await Task.Run(() => { _hood = STCSession.GetNeighborhood(); _hood.VectorChanged += _hood_VectorChanged; STCSession.LockNeighborhood(); IEnumerator<object> en = _hood.GetEnumerator(); while (en.MoveNext()) { STCSession session = en.Current as STCSession; if (session != null) hood_DiscoveryEvent(session, CollectionChange.ItemInserted); } STCSession.UnlockNeighborhood(); }); } void _hood_VectorChanged(IObservableVector<object> sender, IVectorChangedEventArgs evt) { STCSession session = null; lock (sender) { if (sender.Count > evt.Index) session = sender[(int)evt.Index] as STCSession; } if (session != null) hood_DiscoveryEvent(session, evt.CollectionChange); }
我们添加 hood_DiscoveryEvent() 回调函数以捕获矢量变化事件。 当远程会话可以进行连接,或无法在周围(neighborhood)继续使用时,该回调函数将会发出通知。 当新会话可用时,将会接收到 CollectionChange.ItemInserted 事件;当远程 STCSession 离开周围(neighborhood)或 STCSession 参数发生改变时,将会接收到 CollectionChange.ItemRemoved 和 CollectionChange.ItemChanged 事件。 添加 STCSession.ContainsGadget(),以查看远程设备上是否安装了相同的应用。
private async void hood_DiscoveryEvent(STCSession session, CollectionChange type) { switch (type) { case CollectionChange.ItemInserted: await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { _userList.Add(session); AddPeopleToList(session, "Not Connected"); }); break; case CollectionChange.ItemRemoved: // Handle this case to check if remote users have left the neighborhood. if (_neighborhoodList.Count > 0) { NeighborhoodUsers obj; try { obj = _neighborhoodList.First((x => x.Name == session.User.Name)); await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { _neighborhoodList.Remove(obj); _userList.RemoveAll(x => x.Id.ToString() == session.Id.ToString()); }); } catch { obj = null; } } break; case CollectionChange.ItemChanged: // Handle this case to check if any STCSession data is updated. { STCSession item; try { item = _userList.First(x => x.Id == session.Id); } catch { item = null; } if (item != null) item.Update(session); break; } default: break; } }
邀请(Invitation)
我们已经知道了如何发现远程用户(设备),现在应该了解发送连接的流程。 在英特尔 CCF,该流程成为“邀请”。 在英特尔 CCF 3.0, 发送和接收“邀请”通过 STCInitiator 和 STCResponder 进行控制。 STCInitiator 用于向远程用户发送“邀请”,STCResponder 用于响应入站请求。 远程用户接收请求后,将成功建立英特尔 CCF 连接。 使用 STCInitiator 对象发送“邀请”没有任何限制。 一个对象可以发送多个“邀请”。
我们将在以下函数中介绍向远程用户发送邀请。
private void InitializeInitiator(STCApplicationId appId) { initiator = new STCInitiator(appId, true); initiator.InviteeResponded += initiator_InviteeResponded; initiator.CommunicationStarted += initiator_CommunicationStarted; initiator.Start(); }
设置完所有调用处理程序后,则调用 STCInitiator.Start() API。 现在,如要向发现的远程用户发送“邀请”,则需要调用 STCInitiator.Invite() API。
initiator.Invite(_userList[itemListView.Items.IndexOf(e.ClickedItem)].Id);
如要查看所发送的“邀请”的状态,需要实施 STCInitiator.InviteeResponded() 回调。
async void initiator_InviteeResponded(STCSession session, InviteResponse response) { await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { switch(response) { case InviteResponse.ACCEPTED: // You are connected to the user. break; case InviteResponse.REJECTED: //Invite was rejected by the remote user. break; case InviteResponse.TIMEDOUT: //No response. Invite time-out. break; } }); }
“邀请”已发送,远程用户需要接收它。 实施调用 InitializeResponder() 的函数,通过传递 STCApplicationId 对象初始化 STCResponder 对象。 注册 STCResponder.InviteeResponded() 和 STCResponder.CommunicationStarted() 处理程序。 当远程用户响应“邀请”,或当两个英特尔 CCF 用户之间的通信通道成功建立时,将会调用这些处理程序。
private void InitializeResponder(STCApplicationId appId) { responder = new STCResponder(appId); responder.InviteReceived += responder_InviteReceived; responder.CommunicationStarted += responder_CommunicationStarted; responder.Start(); }
远程用户发送的“邀请”可在 STCResponder.InviteReceived() 回调中接收。 接收到“邀请”后,可以接受也可以拒绝。 为响应该“邀请”,需要调用 STCResponder.RespondToInvite() API。
STCResponder.RespondToInvite() API is called. async void responder_InviteReceived(STCSession session, int inviteHandle) { if ((_initiatorDataStream == null) && (_responderDataStream == null)) { try { if (!checkPopUp) { _inviteHandle = inviteHandle; _session = session; Debug.WriteLine("Several windows " + _inviteHandle); checkPopUp = true; await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { InviteeName.Text = session.User.Name + " wants to connect"; InviteePopup.IsOpen = true; checkPopUp = true; }); } } catch (Exception ex) { Debug.WriteLine(ex.Message); } } else { responder.RespondToInvite(session, inviteHandle, false); } }
通信和数据传输
发送和接收“邀请”后,initiator_CommunicationStarted() 和 responder_CommunicationStarted() 回调将提供流处理(Stream handle)。 我们将使用该 NetStream 处理在两个互联用户之间传输数据。 如要获得数据流处理,则需要实施 initiator_CommunicationStarted() 和 responder_CommunicationStarted() callbacks 并创建 NetStream 对象。 可以注册对 NetStream.StreamClosed 和 NetStream.StreamSuspended 事件的回调。 当通信通道关闭或暂停时,将会接收到这些事件。
void initiator_CommunicationStarted(CommunicationStartedEventArgs args) { _initiatorDataStream = args.Stream; objWrapper.SetStream(_initiatorDataStream); _initiatorDataStream.StreamClosed += DataStream_StreamClosed; _initiatorDataStream.StreamSuspended += DataStream_StreamSuspended; _initiatorDataStream.DataReady += objWrapper.StreamListen; } void responder_CommunicationStarted(CommunicationStartedEventArgs args) { _responderDataStream = args.Stream; objWrapper.SetStream(_responderDataStream); _responderDataStream.StreamClosed += DataStream_StreamClosed; _responderDataStream.StreamSuspended += DataStream_StreamSuspended; _responderDataStream.DataReady += objWrapper.StreamListen; } private async void DataStream_StreamClosed(int streamId, Guid sessionGuid) { await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { UpdateConnectionStatus(_session.User.Name, "Not Connected"); if (_inviter) { _initiatorDataStream.Dispose(); _initiatorDataStream = null; _inviter = false; } else if (_responder) { _responderDataStream.Dispose(); _responderDataStream = null; _responder = false; } if (isTransferFrame) { if (this.Frame != null && this.Frame.CanGoBack) this.Frame.GoBack(); } ResetUIScreen(); }); }
现在,我们来看一下数据传输流程。 首先,我们选择想要发送的文件。 对于这一点,我开发了自己的文件管理器,但是选择文件更简单的方式是使用 FileOpenPicker。 选择文件后,将数据编写至接收到的 NetStream 处理中。 如要在通信通道上编写数据,则需要使用 NetStrem.Write()。
async void SendFileData()
async void SendFileData() { uint size = 1024 * 4; byte[] buffer = new byte[size]; int totalbytesread = 0; using (Stream sourceStream = await storedfile.OpenStreamForReadAsync()) { do { int bytesread = await sourceStream.ReadAsync(buffer, 0, buffer.Length); _objNetStream.Write(buffer, (uint)bytesread); totalbytesread += bytesread; TransferedBytes = totalbytesread; if (args.nState != TransferState.FT_SEND_PROGRESS) { args.nState = TransferState.FT_SEND_PROGRESS; args.FileName = storedfile.Name; args.FileSize = (int)storedfileProperties.Size; args.DataBuffer = null; args.readbytes = 0; OnTransferUpdates(args); } } while (totalbytesread < sourceStream.Length); } }
如要接收文件,需要实施 NetStream.Read() 事件回调,这已在“通信”部分进行了介绍。
readBytes = _objNetStream.Read(receivebuffer, (uint)bytesToRead);
最后,我希望该信息能够帮助你了解英特尔 CCF SDK,并希望你能够使用该 SDK 为 Windows 8 和 Android 开发出色的应用。
英特尔和 Intel 标识是英特尔在美国和/或其他国家的商标。
英特尔公司 © 2014 年版权所有。 所有权保留。
* 其他的名称和品牌可能是其他所有者的资产。