在一般的通訊系統中,我們都是通過網路來傳遞尺寸較小的單個資訊(數十個位元組,最大也就幾K或幾十K),但是有時候,需要傳遞的資訊的個頭很大,比如在記憶體中產生的一個資料報表,可能有數M之大。我們將這樣的大尺寸資訊稱為“大資料區塊資訊”(Blob)。
一.發送Blob的幾種方案
在ESFramework/ESPlus中,發送的自訂資訊的最大尺寸是有限制的,這個限制源於架構對單個訊息的最大尺寸有限制(預設為100K,可以通過GlobalUtil的SetMaxLengthOfMessage靜態方法進行修改)。 在之前的版本中,如果要發送巨大尺寸的訊息,有以下幾個方案:
(1)修改架構允許的單個訊息的最大尺寸的預設值,比如從100K改為10M。
(2)通過檔案中轉。即將要發送的大尺寸資訊先儲存到一個檔案中, 然後使用ESPlus提供的傳送檔案的功能將其傳遞給對方。
(3)在發送方可以手動拆分資訊,逐個發送資訊片段,然後在接收方將接收到的片段拼接為一個完整的資訊。
我們應當極力避免方案(1)的做法。我們建議,最好不要通過單個訊息來發送一個巨大的Blob。雖然,TCP串連允許我們一次性將一個大尺寸的資料交給它去發送,但是對某些系統而言,這樣做是很危險的。為什麼了?因為當這個大尺寸的資料還沒有發送完畢時,那麼,在該TCP串連上繼續發送任何其它資料,都會被阻塞。這意味著,如果大尺寸的資料發送需要30s,那麼在這30秒內,整個通道都被Blob獨佔,無法發送任何其他資料,包括可能必須的緊急命令資訊。
而且,方案(1)需要架構預留更多的記憶體作為資料緩衝區,在某些時候,這可能是一種很大的浪費。
方案(2)涉及到硬碟IO,而且以檔案作為中轉,不夠直觀。
方案(3)是最佳的,Blob的片段可以和其他的資訊交叉發送。但是,手動實現該方案還是比較繁瑣的。幸運的是在最新的ESPlus 3.0 中,已經內建了該方案的實現。
二.ESPlus 3.0 提供的發送Blob方法
ESPlus 3.0在自訂資訊空間提供了發送Blob的幾個API:
1.ICustomizeOutter的SendBlob方法:用於在用戶端將Blob資訊發送給其他線上使用者或伺服器。
/// <summary>
/// 向線上使用者或伺服器發送大的資料區塊資訊。直到資料發送完畢,該方法才會返回。如果擔心長時間阻塞調用線程,可考慮非同步呼叫本方法。
/// </summary>
/// <param name="targetUserID">接收訊息的目標使用者ID。如果為null,表示接收者為伺服器。</param>
/// <param name="informationType">自訂資訊類型</param>
/// <param name="blobInfo">大的資料區塊資訊</param>
/// <param name="fragmentSize">分區傳遞時,片段的大小</param>
void SendBlob(string targetUserID, int informationType, byte[] blobInfo ,int fragmentSize);
2.ICustomizeController的SendBlob方法:用於在服務端將Blob資訊發送給某個線上使用者。
/// <summary>
/// 向ID為userID的線上使用者發送大資料區塊資訊。直到資料發送完畢,該方法才會返回。如果擔心長時間阻塞調用線程,可考慮非同步呼叫本方法。
/// </summary>
/// <param name="userID">接收訊息的使用者ID</param>
/// <param name="informationType">自訂資訊類型</param>
/// <param name="blobInfo">大資料區塊資訊</param>
/// <param name="fragmentSize">分區傳遞時,片段的大小</param>
void SendBlob(string userID, int informationType, byte[] blobInfo, int fragmentSize);
對於發送Blob資訊,要注意以下幾點:
(1)選擇合適的片段大小(fragmentSize)。fragmentSize的參數的值取決於網路的暢通狀態和我們期望的單個片段發送所需的時間。
(2)無論是發送方、還是接收方都需要將Blob資訊當作一個整體。在傳送Blob片段的過程中,如果接收方或發送方任何一方掉線,則整個Blob資訊的傳送都會被取消。接收方決不會提交不完整的blob資訊給自訂資訊處理器(ICustomizeHandler)去處理。
(3)SendBlob方法是在當前調用線程中,逐個發送Blob片段的。如果Blob資訊很大,而又不想阻塞調用線程,請使用非同步呼叫。
我們以常見的發送圖片為例,由於大一點的圖片可能有幾M,所以通常採用Blob分區段發送。我們使用非同步呼叫的方式:
private RapidPassiveEngine rapidPassiveEngine = ...;
public static int ImageInformationType = 101;
public void SendImage(Image img, string destUserID)
{
MemoryStream memoryStream = new MemoryStream() ;
img.Save(memoryStream, img.RawFormat);
byte[] blob = memoryStream.ToArray();
memoryStream.Close();
CbGeneric<byte[], string> cb = new CbGeneric<byte[], string>(this.SendBlobThread);
cb.BeginInvoke(blob, destUserID, null, null);
}
private void SendBlobThread(byte[] blob ,string destUserID)
{
this.rapidPassiveEngine.CustomizeOutter.SendBlob(destUserID, ImageInformationType, blob, 2048);
}
(4)在接收方,無論接收的是普通自訂資訊,還是Blob資訊,都會提交給同一個方法(ICustomizeHandler.HandleInformation方法)處理,沒有分別。
我們可以在接收方像下面這樣處理剛才發送的圖片:
public void HandleInformation(string sourceUserID, int informationType, byte[] info)
{
if (informationType == ImageInformationType)
{
MemoryStream memoryStream = new MemoryStream(info) ;
Image img = Image.FromStream(memoryStream);
memoryStream.Close();
//...... 後續的商務邏輯
}
}
三.Blob資訊與P2P
如果收發Blob資訊的雙方是兩個線上的使用者,並且這兩個使用者之間已經建立了P2P通道,那麼Blob資訊將直接經過P2P通道傳送,而不經過伺服器中轉。這和普通的自訂資訊是沒有分別的。(關於如何在兩個用戶端之間建立P2P通道,請參考ESFramework 開發手冊(04) -- 可靠的P2P )
閱讀 更多ESFramework開發手冊系列文章。
-----------------------------------------------------------------------------------------------------------------------------------------------
下載免費版本的ESFramework 以及 demo源碼
關於ESFramework的任何問題,歡迎聯絡我們:
電話:027-87638960
Q Q:372841921
郵件:esframework@oraycn.com