利用WCF的P2P共享剪贴板上的数据

简介:

WCF与P2P   

    WCF是用来实现数据通信的,这篇文章中。我将带领大家进入WCF的P2P的世界。通过一个实例,说明WCF中使用P2P。

    首先让我们了解一下什么是P2P。详细见:P2P。这里根据我的理解,结合WCF简单的叙述一下。一般使用WCF,客户端与客户端交互都要使用一个服务端作为中间站。客户端将数据传递给服务端,服务端再转发给其他的客户端。很明显,这显然加重了服务端的负担。P2P是解决这个问题的。每一个客户端既可以是接受数据的客户端,又是上传数据的服务端。用过PPS和迅雷的童鞋就能很容易的理解了,这两个软件既要上传数据,又要下载数据。看下面两幅图:

 

图一:围绕中心服务器打转

 

图二:P2P分布

实例的功能

    我实现的这个例子的功能是:在一个局域网内,有很多台电脑。当其中一台电脑实现了复制或者剪贴,在其他的电脑上将显示复制和剪贴的数据。你可以实现Ctrl+C或者Ctrl+X进行数据复制或者剪贴。在其他的电脑上有一个窗体专门显示你复制或者剪贴的数据(限定了文本数据)。

    实现这个程序有两个难点:

    1、如何监听Ctrl+C或者Ctrl+X等事件

    2、不需要特定的服务端(不通过WCF的双工通信),如果通过P2P去实现数据通信。

实例的实现:

    下面根据这两个难点来展开去实现。

   一、事件监听:

    1、在WindowsForm应用程序中,protected override void WndProc(ref System.Windows.Forms.Message m)方法可以供我们去重载来实现事件的监听。判断Message的编号,如果是复制或者剪贴事件。我们就去通过WCF的P2P服务来广播剪贴板中的信息。代码如下:

        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            // defined in winuser.h
            const int WM_DRAWCLIPBOARD = 0x308;
            const int WM_CHANGECBCHAIN = 0x030D;

            switch (m.Msg)
            {
                case WM_DRAWCLIPBOARD:
                    DisplayClipboardData();
                    SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                    break;

                case WM_CHANGECBCHAIN:
                    if (m.WParam == nextClipboardViewer)
                        nextClipboardViewer = m.LParam;
                    else
                        SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                    break;

                default:
                    base.WndProc(ref m);
                    break;
            }
        }

    2、然后提交给User32.dll处理:

        [DllImport("User32.dll")]
        protected static extern int SetClipboardViewer(int hWndNewViewer);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

    通过上面的代码,我们解决了第一个问题:如何监听Ctrl+C或者Ctrl+X等事件。

     二、在WCF中,我们要通过NetPeerTcpBinding实现P2P。

     下面我就一步一步的实现这个P2P的应用服务。

    1、定义契约和实现契约:

   [ServiceContract(CallbackContract = typeof(IShare))]
    public interface IShare
    {
       [OperationContract(IsOneWay = true)]
       void ShareClipboard(string type,string message);
    }
    public class ShareImplementation : IShare
    {

        private static Form m_receiverForm;
        private  static ClipEventHandler m_OnClipReceive;
        public void ShareClipboard(string type,string message)
        {
            try
            {

                m_receiverForm.Invoke(m_OnClipReceive, type, message);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }
        }

       public void SetForm(Form form,ClipEventHandler theCallback)
       {
           m_receiverForm = form;
      
           m_OnClipReceive = theCallback;
       }
    }
   使用一个类来管理这个服务:
    public class Peer
    {
        public string Id { get; private set; }

        public IShare Channel;
        public ShareImplementation Host;
        public ClipEventHandler clipeventhandler;
        public Form form;
        public Peer(string id)
        {
            Id = id;
        }


        private DuplexChannelFactory<IShare> _factory;
           public void StartService()
    {
        var binding = new NetPeerTcpBinding();
        binding.Security.Mode = SecurityMode.None;
 
        var endpoint = new ServiceEndpoint(
            ContractDescription.GetContract(typeof(IShare)),
            binding,
            new EndpointAddress("net.p2p://SimpleP2P"));

        Host = new ShareImplementation();
        Host.SetForm(form,clipeventhandler);
        _factory = new DuplexChannelFactory<IShare>(new InstanceContext(Host), endpoint);
 
        var channel = _factory.CreateChannel();
 
        ((ICommunicationObject)channel).Open();
 
        // wait until after the channel is open to allow access.
        Channel = channel;
    }

    public void StopService()
    {
        ((ICommunicationObject)Channel).Close();
        if (_factory != null)
            _factory.Close();
    }

    private readonly AutoResetEvent _stopFlag = new AutoResetEvent(false);
    public void Run()
    {
        Console.WriteLine("[ Starting Service ]");
        StartService();

        Console.WriteLine("[ Service Started ]");
        _stopFlag.WaitOne();

        Console.WriteLine("[ Stopping Service ]");
        StopService();

        Console.WriteLine("[ Service Stopped ]");
    }

    public void Stop()
    {
        _stopFlag.Set();
    }
    }

在WindowsForm中,通过DisplayClipboardData()方法来调用此服务,代码如下。

        void DisplayClipboardData()
        {
            try
            {
                IDataObject iData = new DataObject();

                string type = "",message="";
              
                iData = Clipboard.GetDataObject();

                if (Clipboard.ContainsText())
                {
                    message = (string)iData.GetData(DataFormats.Text);
                }


          
                if (peer != null && peer.Channel != null)
                {
                    peer.Channel.ShareClipboard("text", message);
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }
        }

还有就是一个方法来接受信息方法AddToClip,代码如下:

        public void AddToClip(string type, string message)
        {

            if (type == "rtf")
            {
                IDataObject iData = new DataObject(DataFormats.Rtf, message);
                richTextBox1.Rtf = (string)iData.GetData(DataFormats.Rtf);
                //richTextBox1.Rtf = message;
            }
            else if (type == "text")
                richTextBox1.Text = message;
            else
                richTextBox1.Text = "[Clipboard data is not RTF or ASCII Text]";

            richTextBox1.Text = message;
        }

由于WCF得回调和WindowsForm的主线程不是一个,故使用了一个委托:

    public delegate void ClipEventHandler(string type ,string clipData);

其他详细见代码。

实例的效果:

    在电脑zhuqilin上复制文本数据

A

   在电脑Colin上显示zhuqilin上复制的数据:

B

总结:

      上星期用WCF的双工实现了一个音频聊天室的程序。有园友提出点对点的视频、语音、聊天用P2P去实现效率和性能更好,故研究了一下WCF的P2P。本文就是一个简单的WCF的P2P的例子。

扩展:

    这篇文章只实现了文字剪贴板的共享功能。如果你有兴趣,可以进一步扩展。

        扩展1:数据直接传递到其他电脑的剪贴板上,可以直接Ctrl+V粘贴。

        扩展2:现在只是实现文字剪贴板的共享。扩展成文件、视频、图片都可以。

    效果可以是:如果局域网的两个端点机器通过共享自己的剪贴板。在A机器上复制文件,在B机器上可以直接粘贴。

    写这个例子的灵感来自RealVNC。用过RealVNC的童鞋都知道,无论局域网还是外网,只要两台pc建立连接,就能共享剪贴板上的数据了。

最后:建立P2P和打开P2P管道需要时间,故在运行这个程序的之后,需要等上一段时间才能共享你的剪贴板。如果有建议请留言,有帮助请推荐。thx。




本文转自麒麟博客园博客,原文链接:http://www.cnblogs.com/zhuqil/archive/2010/06/20/wcf-p2p-demo.html,如需转载请自行联系原作者

相关文章
WCF基础教程(四)——数据契约实现传送自定义数据类型
WCF基础教程(四)——数据契约实现传送自定义数据类型
83 0