我的邻居,Windows 联系人以及邀请人都允许一个应用程序开始一个合作性的活动。在.NET Framework 3.5 之前,开发人员需要调用非托管APIs来使用这些特性。这意味着他们需要使用C++开发或者至少创建互操作程序集(使用P/Invoke)在.NET Framework 3.5 中使用的经历已经通过引入可以使用内建于Windows Vista中的我的邻居,Windows 联系人以及邀请人架构的托管库而取代。这些库在一个成为System.Net.PeerToPeer的新的命名空间中提供。一个开发人员需要向System.Net程序集中添加一个引用来使用这些新库。我们将查看一个叫做对等会话的简单的示例程序来了解如何使用这些新特性。图片12.14显示了对等会话应用程序。
图片12.14 对等会话示例程序
一个应用程序首先要考虑的是使用Windows Vista中的合作架构来注册它自己。如果你想要发送一个邀请来运行这个应用程序那么就需要它。列表12.9显示了对等会话程序如何使用对等架构注册自己。为了完成这个我们使用在System.Net.PeerToPeer.Collaboration命名空间找到的PeerCollaboration静态类来调用注册方法。这个方法接受一个PeerApplication类的实例。这个类的实例是对等应用程序的描述,包括一个应用程序标识符和一个描述信息。
列表12.9 注册一个对等应用程序
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.PeerToPeer.Collaboration; namespace PeerChat { public class PeerChatApplication { private static PeerApplication PeerChatPeerApplication; private static Guid PeerChatAppId = new Guid("4BC6F59E-124E-4e75-8CD9-BB75BCA78CA8"); private static string PeerChatDescription = "A sample peer networking application"; static PeerChatApplication() { PeerChatPeerApplication = new PeerApplication(PeerChatAppId, PeerChatDescription, null, System.Windows.Forms.Application.ExecutablePath, null, PeerScope.All); } public static void Register() { PeerApplicationCollection peerAppsColl = PeerCollaboration.GetLocalRegisteredApplications( PeerApplicationRegistrationType.AllUsers); //You gotta love LINQ! It is so cool! IEnumerable<PeerApplication> findPeerApp = from peerApp in PeerCollaboration.GetLocalRegisteredApplications( PeerApplicationRegistrationType.AllUsers) where peerApp.Id == PeerChatAppId select peerApp; if (findPeerApp.Count<PeerApplication>() != 0) { PeerCollaboration.UnregisterApplication( PeerChatPeerApplication, PeerApplicationRegistrationType.AllUsers); } PeerCollaboration.RegisterApplication( PeerChatPeerApplication, PeerApplicationRegistrationType.AllUsers); } public static void UnRegister() { PeerCollaboration.UnregisterApplication( PeerChatPeerApplication, PeerApplicationRegistrationType.AllUsers); } } }
一个应用程序下一步可能要做的是显示在它的本地子网内的人员列表以便于用户可以邀请他们来参加活动。这是通过枚举加入到我的邻居架构中的人来完成的。一个用户需要被签名到我的邻居内才可以收集在他们附近的人。为了帮助这个过程,我们创建了一个帮助者类来保证用户在第一次请求之前已经签名了。列表12.10 显示了PeopleNearMeHelper类。这个类调用PeerCollaboration静态类中的SignIn方法来保证用户已经签入到我的邻居中。当用户签入以后,用户可以自由调用GetPeersNearMe静态方法,它会返回一个PeerNearMe实例集合。一个PeerNearMe集合是一个在本地子网内登陆到Peer Near Me内的人的描述。
列表12.10 People Near Me
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.PeerToPeer.Collaboration; namespace PeerChat { public class PeopleNearMeHelper { public PeopleNearMeHelper() { PeerCollaboration.SignIn(PeerScope.All); } public PeerNearMeCollection PeopleNearMe { get { return PeerCollaboration.GetPeersNearMe(); } } } }
下一步我们需要向另外一个人发送一个邀请以便于它可以参与进来。对等会话应用发送一个邀请给其他人来一起开始一次会话。为了实现这个,我们不仅仅需要发送邀请,我们也需要发送一些额外的信息来引导通信过程。记着在两个对等通信者之间实际通信是由WCF和对等信道架构处理的。这意味着我们需要额外的信息,比如网状网络的名字以及密码来继续通信。额外的信息与邀请过程一起发送。列表12.11显示了如何从Windows Vista的合作架构发送并接收邀请。一次邀请使用一个PeerNewMe实例的Invite或者InviteAsync方法来发送。强烈建议你使用InviteAsync方法;否则,用户接口被阻塞,等待用户接受邀请。这两个方法都有一个选项来以一个字节数组形式发送额外的数据。在我们的情况,我们把网状网络名字和密码打包都一个字节流中并一并发送出去。通过与邀请一起发送网状网络名字和密码更多考虑是信息可能暴露。不要担心!People Near Me架构通过一个加密连接传输信息。
列表12.11 发送并接收邀请
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.PeerToPeer.Collaboration; using System.IO; namespace PeerChat { public class InvitationHelper { private static PeerApplication PeerChatPeerApplication; private static Guid PeerChatAppId = new Guid("4BC6F59E-124E-4e75-8CD9-BB75BCA78CA8"); private static string PeerChatDescription = "A sample peer networking application"; static InvitationHelper() { PeerChatPeerApplication = new PeerApplication(PeerChatAppId, PeerChatDescription, null, System.Windows.Forms.Application.ExecutablePath, null, PeerScope.All); } public static PeerInvitationResponseType Invite(PeerNearMe personTo, Guid chatId, Guid meshPassword) { byte[] data; using (MemoryStream ms = new MemoryStream()) { using (StreamWriter sw = new StreamWriter(ms)) { sw.Write(chatId.ToString()); sw.WriteLine(); sw.Write(meshPassword.ToString()); } data = ms.ToArray(); } PeerInvitationResponse response = personTo.Invite(PeerChatPeerApplication, "You are being invited to chat.", data); return response.PeerInvitationResponseType; } public static void InviteAsync(PeerNearMe personTo, Guid chatId, Guid meshPassword) { byte[] data; using (MemoryStream ms = new MemoryStream()) { using (StreamWriter sw = new StreamWriter(ms)) { sw.Write(chatId.ToString()); sw.WriteLine(); sw.Write(meshPassword.ToString()); } data = ms.ToArray(); } object userToken = Guid.NewGuid(); personTo.InviteAsync(PeerChatPeerApplication, "You are being invited to chat.", data, userToken); } public static bool IsLaunched { get { return (PeerCollaboration.ApplicationLaunchInfo != null) && (PeerCollaboration.ApplicationLaunchInfo.Data != null) } } public static Guid ChatId { get { Guid chatId; using (MemoryStream ms = new MemoryStream(PeerCollaboration.ApplicationLaunchInfo.Data)) { using (StreamReader sr = new StreamReader(ms)) { string chatIdString = sr.ReadLine(); string meshPassword = sr.ReadLine(); chatId = new Guid(chatIdString); } } return chatId; } } public static Guid MeshPassword { get { Guid meshPassword; using (MemoryStream ms = new MemoryStream(PeerCollaboration.ApplicationLaunchInfo.Data)) { using (StreamReader sr = new StreamReader(ms)) { string chatIdString = sr.ReadLine(); string meshPasswordString = sr.ReadLine(); meshPassword = new Guid(meshPasswordString); } } return meshPassword; } } } }
最后要考虑的是如何确定一个应用程序是否基于一个来自于People Near Me的邀请运行。为了实现这个我们需要获得一个PeerApplicationLaunchInfo类的实例。这在PeerCollaboration静态类的ApplicationLaunchInfo属性中可用。列表12.11也显示了如何使用这个类来确定是否应用由于邀请被运行以及如何访问由邀请发送的额外数据。