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上復制文本數據
在電腦Colin上顯示zhuqilin上復制的數據:
總結:
上星期用WCF的雙工實現了一個音頻聊天室的程序。有園友提出點對點的視頻、語音、聊天用P2P去實現效率和性能更好,故研究了一下WCF的P2P。本文就是一個簡單的WCF的P2P的例子。
擴展:
這篇文章只實現了文字剪貼板的共享功能。如果你有興趣,可以進一步擴展。
擴展1:數據直接傳遞到其他電腦的剪貼板上,可以直接Ctrl+V粘貼。
擴展2:現在只是實現文字剪貼板的共享。擴展成文件、視頻、圖片都可以。
效果可以是:如果局域網的兩個端點機器通過共享自己的剪貼板。在A機器上復制文件,在B機器上可以直接粘貼。
寫這個例子的靈感來自RealVNC。用過RealVNC的童鞋都知道,無論局域網還是外網,只要兩台pc建立連接,就能共享剪貼板上的數據了。
最後:建立P2P和打開P2P管道需要時間,故在運行這個程序的之後,需要等上一段時間才能共享你的剪貼板。如果有建議請留言,有幫助請推薦。thx。
出處:http://zhuqil.cnblogs.com
本文配套源碼