現在我們将在 ESFramework Demo -- 檔案傳送Demo 的基礎上,使用ESPlus提供的第四個武器,為其增加P2P通信的功能。在閱讀本文之前,請務必先掌握 ESFramework 開發手冊(04) -- 可靠的P2P
一文中介紹的P2P的基礎知識以及相關API的用法。
本Demo主要示範以下功能:
(1)建立基于TCP的P2P通道
(2)建立基于UDP的P2P通道(内部使用可靠的UDP)
(3)使用P2P通道發送消息和傳送檔案
一.服務端
在P2P打洞的過程中,服務端會參與協助P2P通道的建立,整個過程是由ESFramework/ESPlus内部自動完成的,而這個過程對于架構使用者是透明的。P2P通道建立後,用戶端與用戶端之間的通信就與伺服器沒有任何關系了。是以我們直接把上一個demo的服務端拿過來用,不需要做任何修改。
二.用戶端
用戶端主要使用IRapidPassiveEngine提供的P2PController來查詢和控制P2P通道的狀态。
嘗試建立P2P通道
正如
一文中介紹的,并不是所有的用戶端之間的P2P通道都可以建立成功,建立P2P通道是一個嘗試的過程,IP2PController的P2PConnectAsyn方法就是嘗試與目标使用者建立P2P通道。
時機很重要。在何時建立P2P通道了?一般而言,是在兩個用戶端需要高頻通信之前,調用P2PConnectAsyn進行嘗試建立。
在本Demo中,我們是在每次打開與目标使用者的聊天視窗的時候,來嘗試建立P2P通道的。如下所示:

void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (e.Button != System.Windows.Forms.MouseButtons.Left)
{
return;
}
ListViewHitTestInfo info = this.listView1.HitTest(e.Location);
if (info.Item != null)
{
//嘗試與目标使用者建立P2P通道
this.rapidPassiveEngine.P2PController.P2PConnectAsyn(info.Item.Text);
ChatForm form = this.chatFormManager.GetForm(info.Item.Text);
......
}
}

使用P2PConnectAsyn方法開始打洞時,若雙方位于同一區域網路,一般會建立起基于TCP的P2P通道;若雙方位于不同的網絡,一般會建立起基于UDP的P2P的通道。如果與目标使用者之間已經存在可用的P2P通道,則P2PConnectAsyn将不再做任何動作,而直接傳回。
為了獲得P2P通道建立成功或失敗以及後續P2P通道被關閉的通知,demo中我們在MainForm的Initialize方法中預定了P2PController的P2PChannelOpened和P2PChannelClosed事件。
//預定P2P Channel建立成功的事件
this.rapidPassiveEngine.P2PController.P2PChannelOpened += new CbGeneric<P2PChannelState>(P2PController_P2PChannelOpened);
//預定P2P Channel關閉時的事件
this.rapidPassiveEngine.P2PController.P2PChannelClosed += new CbGeneric<P2PChannelState>(P2PController_P2PChannelClosed);
當P2P通道建立成功或被關閉時,本demo通過修改對應聊天視窗的Title文字來顯示這種狀态。比如,當P2P通道建立成功時,聊天視窗的Title顯示如下:
通常,如果兩個用戶端位于同一個LAN,或者有一個用戶端直接位于Internet上,則它們之間的P2P通道是基于TCP的;否則,建立的P2P通道是基于UDP的。
ChatForm提供了ShowP2PChannelState方法來顯示與聊天對象之間的P2P通道狀态。

///<summary>
/// 顯示P2P連接配接的狀态
///</summary>
public void ShowP2PChannelState(P2PChannelState state)
{
this.Text = (state != null) ? string.Format("正在與{0}對話中【{1}直連:{2}】...", this.friendID, state.ProtocolType, state.DestIPE) : string.Format("正在與{0}對話中...", this.friendID);
}

請注意,當與目标使用者之間沒有P2P通道時,P2PController的GetP2PChannelState方法傳回的是null。
觀察P2P通信
當P2P通道建立成功後,兩個使用者之間的後續通信将經過P2P通道傳送,在本Demo中,表示後續的聊天消息以及檔案傳送都将通過P2P通道進行。
那麼,如何判斷消息是通過伺服器中轉的,還是經過P2P通道直接傳送的了?我們常用的有兩個簡單的方法。
(1)觀察伺服器的MainServerForm界面。
如果消息是經過伺服器中轉的,那麼界面上顯示消息接收者使用者對應的“下載下傳次數”、“上傳次數”、“最後一次下載下傳時間”等都會跟着發生變化。就本例來說,每當你給對方發送一個聊天消息,如果是經過伺服器中轉,那麼界面上顯示的對方的下載下傳次數會增加1,自己的上傳次數也增加1,自己的最後一次上傳時間也會變化。而如果消息是通過P2P通道傳送的,這些資料就不會受影響。
(2)檢視資源螢幕
如果是Win7的系統,任務管理器“性能”顯示中提供了“資源螢幕”,可以監控網絡的活動。使用它,我們就可以看到應用程式在和哪些機器進行通信。如下圖所示:
上面是我們另一個P2P應用demo的截圖,圖中59.175.145.163是伺服器的IP,而我們看到OMCS.ClientDemo.exe接收資料的主要流量來自于ZY-PC這台電腦,這表示兩個用戶端之間的資料是經過P2P通道傳送的,沒有通過伺服器中轉。之是以圖中顯示的用戶端與伺服器之間還有微小的流量,那是由類似定時心跳消息等産生的。
如果不是Win7系統,也可以通過安裝網絡監控軟體(如NetLimiter)來檢視這些資訊。
三.源碼下載下傳
ESFramework.Demos.P2P 源碼閱讀
更多ESFramework開發手冊系列文章。
-----------------------------------------------------------------------------------------------------------------------------------------------
關于ESFramework的任何問題,歡迎聯系我們:
電話:027-87638960
Q Q:372841921
郵件:
[email protected]