图 3 显示了完成后的应用程序。步骤 1 和 2 是开发任何 Windows 窗体应用程序时所必需的步骤,因此我将不在这里对其说明。对于任何 Windows Communication Foundation 应用程序,开发过程的第一步是定义服务合约。PeerChannel 要使用的服务合约类似于其他 Windows Communication Foundation 合约,只不过 PeerChannel 需要所有的 OperationContractAttribute 批注都将 IsOneWay 实例属性设置为 true。此属性规定消息的接收方不应发送回复。如果想要接收方发送回复,可以将服务合约定义为双向合约,但每个 OperationContractAttribute 批注仍必须将 IsOneWay 实例属性设置为 true。就此例而言,我不会创建一个双向合约(Windows SDK 中有几个双向合约的示例)。我要使用的合约如下所示:
| [ServiceContract] interface IPictureViewer { [OperationContract(IsOneWay = true)] void SharePicture(Stream stream); } |

图 3 PictureViewer P2P 应用程序
请注意,SharePicture 接口方法用 OperationContractAttribute 属性加以批注,并且 IsOneWay 实例属性被设置为 true。SharePicture 操作将 System.IO.Stream 视为一个参数,因为此操作将被用于向网格中其他节点传送图片的字节。
在定义了我们的服务合约后,现在就该添加 Windows Communication Foundation 代码,该代码会将我们的应用程序连接到 PeerChannel 网格并且被动等待来自网格的消息。首先,在窗体中实现新定义的服务合约。然后,定义类型 ServiceHost 的字段。所接收的消息将被发送到 frmPictureViewer 类型的单个实例。要表明此功能,我必须将正确的 ServiceBehavior 分配给 frmPictureViewer 类型。这两个步骤如下所示。
实施服务合同
| [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] public partial class frmPictureViewer : Form, IPictureViewer { // 托管接收者 ServiceHost host; // 接收消息时将要调用的方法 public void SharePicture(Stream stream) { // 获取图像 Image image = Bitmap.FromStream(stream); // 在窗体的图片框中显示图像 pbView.SizeMode = PictureBoxSizeMode.StretchImage; pbView.Image = image; } ... // 为清晰起见省略其他成员 } |
尝试加入网格和侦听消息
| public frmPictureViewer() { InitializeComponent(); StartReceiving(); } private void StartReceiving() { // 定义网格名并设置与 PNRP 解析器的 // 对等绑定 Uri meshAddress = new Uri("net.p2p://pictureView"); NetPeerTcpBinding binding = new NetPeerTcpBinding(); binding.Resolver.Mode = PeerResolverMode.Pnrp; binding.Security.Transport.CredentialType = PeerTransportCredentialType.Password; binding.MaxReceivedMessageSize = 700000L; host = new ServiceHost(this); host.AddServiceEndpoint(typeof(IPictureViewer), binding, meshAddress); // 定义密码并获取数字签名证书 host.Credentials.Peer.MeshPassword = "JustinSmith"; host.Credentials.Peer.Certificate = GetCertificate(); // 尝试联结和侦听消息 host.Open(); } |
只要调用了 ServiceHost.Open,我们的应用程序就会尝试通过 PNRP 来解析网格名 (pictureView)。此时,我可以通过运行 netsh 命令以列出注册的对等名称来验证我们的 PeerChannel 应用程序是否正在使用 PNRP。如果 PNRP 可以将网格名解析为一个或多个 IP 地址,则我们的应用程序将尝试连接到这些节点。如果不是这样,则该节点将成为网格中的第一个节点。如前所述,现有节点将通过发送欢迎消息或拒绝消息来接受或拒绝连接。这里的重要一点是,这种情况有可能在对 ServiceHost.Open 的调用返回后发生。
将消息发送到其他节点 在共享图片之前,我必须首先加载图片。实现此操作所需的代码是 Windows 窗体基本代码:首先,对 OpenFileDialog 进行实例化,获得一个 Stream,将该 Stream 转换为 Image,然后通过 PictureBox.Image 属性引用 Image。等一下,这不是 SharePicture 方法所执行的操作吗?事实上,确实如此。从本质上说,要将图像加载到 PictureBox 中,我只需要调用 SharePicture 方法,将从 OpenFileDialog.OpenFile 返回的 Stream 作为一个参数传递。
要将包含图片的消息发送到网格中的其他节点,我必须编写几行代码,但是此代码与您在其他任何 Windows Communication Foundation 应用程序中编写的代码几乎相同。起初,我需要在类型 ChannelFactory 和 IPictureViewer 的窗体中定义一些字段。接下来,我需要在窗体的构造函数中将这些变量实例化。这些步骤如下所示。
创建发送基础结构
| ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] public partial class frmPictureViewer : Form, IPictureViewer { ChannelFactory channelFactory; IPictureViewer channel; public frmPictureViewer() { InitializeComponent(); StartReceiving(); } private void StartReceiving() { // 为清晰起见省略其他代码 channelFactory = new ChannelFactory( binding, new EndpointAddress(meshAddress)); channelFactory.Credentials.Peer.MeshPassword = "JustinSmith"; channelFactory.Credentials.Peer.Certificate = GetCertificate(); channel = channelFactory.CreateChannel(); } ... // 为清晰起见省略其他代码 } |
既然我已经构建了自己的发送基础结构,我就可以使用它向网格中的其他节点发送消息。为此,我只需为共享按钮编写一个事件处理程序即可,如下所示:
| private void btnShare_Click(object sender, EventArgs e) { using(MemoryStream stream = new MemoryStream()) { Image image = pbView.Image; image.Save(stream, ImageFormat.Jpeg); // 将图像存储到 stream 中 stream.Position = 0; // 复位位置 channel.SharePicture(stream); // 向网格发送消息 } } |
People Near Me
PNM 是集成在 Windows Vista 之中的一种网格技术,它允许邻近的设备组和人员组相互发现、连接、邀请并进行协作。PNM 特别适用于这样一些任务:在咖啡店与邻座其他几个人一起玩游戏;与同事共享您的桌面;甚至连接到会议室中的投影仪,等等。PNM 提供的这些功能如此强大,我们有理由假设,一旦它被发布,开发人员社区就将会找到新的、具有独创性的方式来利用此技术。重要的是,要注意 PNM 是一项完全自选的网格技术,在默认情况下是关闭状态。
除其他应用程序之外,PNM 体系结构包含了一个称为 p2phost.exe 的 P2P 应用程序。此进程运行时,将通过连接到其他计算机上 p2phost.exe 的实例来创建网格。通常而言,此网格的用途是定向消息传送。更确切地说,PNM 用于解析本地节点并与这些本地节点的子集进行通信。PNM API 作为 Windows API 的一部分提供,并且多半程度上侧重于配置 p2phost.exe 的行为。
总的来说,PNM API 的主要类别包括函数、结构、事件和错误代码,通过这些类别可以向 PNM 注册应用程序、邀请其他人加入协作会话、启动已注册的应用程序、创建持久性合约并邀请不再属于本地的联系人。提要栏中的“真实环境中 People Near Me 的示例”将例示此过程。请注意,不支持应用程序使用 PNM 进行通信。就 PictureViewer 而言,这表示在提要栏中说明的 Tom 和 Harry 的 PictureViewer 实例之间传递的消息仍由 PeerChannel 来处理。
结束语
P2P 应用程序开发是一个涉及范围非常广泛的主题,并且对于多数开发人员而言还相当陌生。随着 Windows Vista 和 .NET Framework 3.0 的发布,P2P 应用程序的传统开发门槛将明显降低。我相信,技术的进步(如 PNRP、IPv6)加上更具生产力的新型平台的问世(如 PeerChannel 和 PNM)将在 P2P 应用程序开发领域开创一个新时代。最终,应用程序将更具协作性,并提供我们起初只能想像的一些功能。