程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 平步青雲:連接管理器如何連接

平步青雲:連接管理器如何連接

編輯:關於.NET

我記得那是會議的第三天。我坐在吹著冷氣的會議室中聽首席項目經理講全新連接管理器技術的奧秘,可能前一晚離開酒吧時有點晚了,想起前兩天超負荷的信息量,我的思維有些混亂。

我腦中冒出的想法是,所有這些有什麼意義呢?我的意思是說,有誰會需要一種以指示設備是否已連接為己任的技術?當時,連接只能通過連接電纜或大型配件實現,需要 PhD 分析網絡協議,需要不厭其煩地進行運算:如果用戶不知道設備已連接,還有什麼資格擁有設備呢?

令人欣慰的是,技術已有了很大的發展,現在普通的移動電話就能夠提供多種奇異的無線技術,比如,可以通過移動電話加熱 40 步內的飯盒中的食物。Windows Mobile 設備在此領域中占據了比較多的一大塊,並與 Windows CE 強占式多任務內核緊密結合,連接管理器團隊的遠見卓識顯而易見。

如果沒有連接管理器,恐怕除了最簡單的網絡環境外,應用程序只能通過為用戶提供選擇列表的方式讓用戶選擇連接,這將使您倒退到需要 PhD 分析網絡協議的階段。連接管理器的出現巧妙地為用戶解決了這一難題 — 畢竟用戶的目的只是希望應用程序能夠正常運行,解決此問題應該是移動運營商和 OEM 的責任,如果他們都不能解決,對此知之甚少的用戶就更加無能為力了。

正確使用連接管理器對於需要網絡數據的 Windows Mobile 應用程序至關重要,主要原因有兩個。首先,並非只有代碼需要連接網絡。現今的很多應用程序都需要連接到網絡,或者至少提供某種形式的聯機能力以下載更新功能或其他功能。這就使設備中的兩個應用程序在某個時刻爭用連接的可能性增加了。對於使用推送或輪詢電子郵件解決方案的企業用戶來說,這種爭用必然會發生。其次,Windows Mobile 設備的銷售和使用范圍覆蓋全球。對於一個獲得全球市場青睐的應用程序來說,它應該具有移動運營商可能夢想到的任何網絡配置,並且據我了解,在涉及到網絡拓撲問題時,移動運營商的想象力是極其豐富的!

我堅信,了解的越多,使用的效果就越好。在本專欄中,我將深入內部介紹連接管理器的一些內部工作,尤其會介紹它何以能在通過廣域手機網連接時增加價值和消除復雜性。這似乎與 C++ 開發人員的相關性更大,因為他們通常直接調用連接管理器 API。托管代碼開發人員擁有很多無需開發人員直接干預即可使用連接管理器的類,如 HttpWebRequest。但是,即使對於 .NET 開發人員來說,了解隱藏在背後的連接管理器如何支持托管代碼,對解決復雜的連接問題也非常有用。

PDP 上下文

大部分網絡復雜性都是因為移動運營商必須按常規考慮到以下三個競爭要求:第一,他們的客戶希望跟得上無線網絡技術的急速變化。第二,他們通過大型基礎結構將無線網絡范圍覆蓋到廣大區域。第三,他們盡量避免耗盡股東的投資。

涉及到分組數據協議 (PDP) 上下文的領域是一個更具挑戰性並直接影響到軟件開發人員的領域。PDP 上下文描述了手機和全球移動通信系統 (GSM) 網絡之間的協議,還包含訪問點名稱 (APN) 和 IP 地址等重要信息。此協議是固定不變的,創建後便不能更改。(Wikipedia 上的“GPRS 核心網絡”文章中有更完整的說明。)

當設備通過向基站發出請求、發送所需 APN 的名稱以建立 PDP 上下文之後,應用程序才可以發送和接收數據。該請求通常轉發至移動運營商的計費網絡中以獲得授權,從而確保用戶的合同有效、可用額度足夠,等等。完成此步驟之後,APN 會提供一個 IP 地址,並會將其傳遞到基站,隨後轉至手機。此時,設備就可以使用分配的 IP 地址發送和接收數據了。

是不是只需要使用一個 PDP 上下文就夠了呢?不一定 — APN 提供了一種以不同方式改變計費或流量限制的簡便方法。例如,可以將一個特定的 APN 作為通往企業專用網絡的直接橋梁,通過運營商的計費系統限制對此 APN 的訪問;或者將一個 APN 用於以 MB 為單位進行計費的系統,另一個 APN 用於“自助”服務。這種做法比較普遍。適用於移動運營商的一個現實示例是,要求所有的推送電子郵件數據通過特定的 APN 而其他 Internet 流量通過另一個 APN。

我在前面提到過,PDP 請求通常通過計費基礎結構進行路由,並且需要花費一些時間。創建上下文需要的時間最長可達 20 秒,實際時間根據運營商和網絡的不同會有所差異。請記住,PDP 上下文是固定不變的,因此 APN 無法以動態方式進行交換,而必須放棄整個上下文,然後重新創建一個新的。

僅就性能來說,有多個並發 PDP 上下文支持不失為一件好事。但是,正如您所料,這需要手機和基站具有一些其他功能才能實現。值得慶幸的是,大多數時下流行的手機都支持三個或更多上下文,但說到基站,可就有點意思了。雖然大部分新站都支持此功能,但許多第一代 3G 基站仍在使用中,這些基站只允許每個手機有一個 PDP 上下文。

這就導致必須有許多復雜的規則,以確保每個應用程序的連接方式都正確,而且,隨著所連接的基站和無線服務級別的變化,所謂的正確方法也常常要改動。

對網絡拓撲進行建模

連接管理器旨在實現以下三個目標:

具有足夠的靈活性和可配置性,以便移動運營商和 OEM 可以描述其多種多樣的網絡拓撲和設備功能。

減少和簡化開發人員選擇和連接到任何網絡內的手機中的網絡資源所必須采取的步驟。

為爭用有限資源的應用程序提供仲裁。

鑒於我先前介紹的 PDP 上下文的復雜性,連接管理器要擴展其支持,以便除支持通用分組無線業務 (GPRS) 之外,還支持 WiFi、撥號、有線甚至桌面傳遞連接,可並不是一項容易實現的目標!

第一步是為連接管理器繪制包含所有可用連接的完整藍圖,然後根據此藍圖通過足夠抽象的方式建模以包含在系統的生命周期中成為主流的各項新興技術。連接管理器提供的一組簡單一致的配置基元允許移動運營商和 OEM 為硬件和網絡拓撲建模,可通過開放移動聯盟 (OMA) 設備管理、OMA 客戶端配置來配置,在某些情況下也可通過設置用戶界面來配置。

以下是簡單的 OMA 客戶端配置 XML 示例,借此您可以對它有個大概的了解:

<wap-provisioningdoc>
 <characteristic type="CM_GPRSEntries">
  <characteristic type="Internet GPRS">
   <parm name="DestId" value="{436EF144-B4FB-4863-A041-8F905A62C572}" />
   <characteristic type="DevSpecificCellular">
    <parm name="GPRSInfoValid" value="1" />
    <parm name="GPRSInfoAccessPointName" value="MyInternetAPN" />
   </characteristic>
  </characteristic>
</wap-provisioningdoc>

我們從元網絡開始介紹基本組件。這是連接管理器使用的最抽象的概念,無法實際映射到網卡或計算機等有形物上。但是,這些項都可視為網絡模型的終點或節點,通常被賦予 Internet、DMZ 或 Walled Garden(圍牆花園)等描述性名稱,並由 GUID 標識。有一個預定義列表包含了常用目標(如“Internet”和“工作”),此列表已在 MSDN 庫中發布並已部署在所有設備中。但是,此列表可完全擴展。在配置 OMA 客戶端配置時,其配置名稱為 CM_Networks。

GPRS 和其他分組數據連接都在其各自的配置部分有所說明,配置部分中包含連接的相關信息,例如 APN 名稱、用戶/密碼信息和許多其他設置。這些設置必須還包含建立連接後即可用的元網絡(稱為目標元網絡)的 ID。分組數據項沒有可用的源網絡,因為源網絡一定依附於設備。CM_GPRSEntries 可用於 OMA 客戶端配置。

WiFi、撥號和可由連接管理器管理的其他硬件有不同的項。VPN 和代理項都有自己的配置,但稍有不同,因為它們提供的是元網絡間的連接 — 存在源元網絡和目標元網絡之分。

我還需要再介紹一個非常重要的配置設置:映射表(CM_Mappings 用於 OMA 客戶端配置)。嚴格來說,此表並不對網絡拓撲的任何相關內容進行建模,而是供應用程序找到需要連接的元網絡。應用程序可能知道自己需要的 URL,但不知道哪個元網絡能夠訪問該 URL,因為能訪問該 URL 的元網絡會因運營商和設備的不同而異。

通過可將提供的 URL 轉換為元網絡 GUID 的 API,映射表將選擇元網絡的任務委托給移動運營商或 OEM。如果不使用映射表,目標元網絡將由用戶硬連線或提供,這樣我們就又要重提依靠用戶這個舊話題了。

圖 1 顯示的示例說明了如何對網絡拓撲和相應的映射表進行建模。映射表是已排序的 URL 模式列表,每個模式都帶有一個元網絡 GUID 並由 ConnMgrMapURL API 對其進行詢問。應用程序將其目標 URL 傳遞給 ConnMgrMapURL,這將從最小 ID 開始遍歷此表,以查找第一個與 URL 匹配的模式。如果找到匹配項,將返回 GUID 並可將其傳遞給 ConnMgrEstablishConnection 以建立連接。

圖 1 示例網絡拓撲

圖 2 顯示了以 XML 格式顯示的映射表的示例。它顯示了六個映射項,501 是第一個。還顯示了可能匹配的大部分模式。雖然模式不是正則表達式,但它可以提供相當大的靈活性。* 用作通配符。因此,舉個例子來說,*://*.operator.*/* 將映射到任何協議,即任何包含 .operator. 後跟一個 / 和任一尾隨頁名這種文本模式的地址。以下 URL 與此目標網絡相匹配:

http://www.operator.com/
rtsp:// rtsp.operator.media.com/20070707_ABHDD3227DD/today_news1.sd
https://my.long.name.operator.da/index.aspx

圖 2 — XML 格式的映射表示例

<wap-provisioningdoc>
 <characteristic type="CM_Mappings">
  <characteristic type="501">
   <parm name="Pattern" value="*://*/*.3gp"/>
   <parm name="Network" value="{D3B2D798-9E69-4B65-A75B-6DDFBECEAAAA}"/>
  </characteristic>
  <characteristic type="610">
   <parm name="Pattern" value="*://*.operator.*"/>
   <parm name="Network" value="{7022E968-5A97-4051-BC1C-C578E2FBA5D9}"/>
  </characteristic>
  <characteristic type="536870912">
   <parm name="Pattern" value="wsp://*/*"/>
   <parm name="Network" value="{7022E968-5A97-4051-BC1C-C578E2FBA5D9}"/>
  </characteristic>
  <characteristic type="553648128">
   <parm name="Pattern" value="wsps://*/*"/>
   <parm name="Network" value="{F28D1F74-72BE-4394-A4A7-4E296219390C}"/>
  </characteristic>
  <characteristic type="570425344">
   <parm name="Pattern" value="*://*.*/*"/>
   <parm name="Network" value="{436EF144-B4FB-4863-A041-8F905A62C572}"/>
  </characteristic>
  <characteristic type="587202560">
   <parm name="Pattern" value="*://*/*"/>
   <parm name="Network" value="{A1182988-0D73-439E-87AD-2A5B369F808B}"/>
  </characteristic>
 </characteristic>
</wap-provisioningdoc>

後四項是 Windows Mobile Standard 中的默認項,後兩項分別標識 Internet(地址中使用 .)流量和工作流量。

當然,對於通用應用程序(例如用戶要在其中輸入許多不同 URL 的浏覽器或媒體播放器)來說,這不失為一種理想的選擇,但對於只使用一個 URL 的應用程序,這是不是小題大做了?即使在僅使用一個 URL 的應用程序中,我也強烈建議您在每次連接時都使用映射表,因為這是獲得世界各地每個網絡配置中的合適元網絡的唯一方法。

建立連接

通過網絡模型,連接管理器就可以展示它的魔力。在本文中搬出 API 文檔的內容講述一遍沒什麼意義,MSDN 庫中的連接管理器 API 文檔可以很好地擔負起解釋說明的任務。我認為重點介紹 API 正常序列和標注出關鍵設置更有意義。

要建立連接,API 序列通常如下:

調用元網絡要連接的 ConnMgrMapUrl。

准備 CONNMGR_CONNECTIONINFO 結構,此結構描述了您希望連接管理器執行的操作。

調用 ConnMgrEstablishConnection(還具有此 API 的同步版本)以創建請求。

監視連接管理器消息並跟蹤任何可能發生的狀態更改。

完成連接後,調用 ConnMgrReleaseConnection to 以關閉請求,並允許對資源進行重新分配。

圖 3 顯示了用於建立連接的 API 序列的簡化示例。CONNMGR_CONNECTION-INFO 結構有幾個很重要的部分,但經常會設置錯誤,有關該完整結構的詳細信息,請參閱 MSDN 庫中的 CONNMGR_CONNECTIONINFO 文章。我在此處介紹其中部分重點內容:

DWORD dwFlags 開發人員需要了解幾種標志。例如,通過代理識別選項,連接管理器可以獲知您的代碼可以與之通信的代理服務器類型。根據這些標志,連接管理器將包括或排除通過這些類型的代理服務器可用的某些元網絡。如有可能,您應該編寫代理識別的代碼(通常 HTTP 代理就足夠了)。這並不難!對於本機 C++ 代碼,使用 InternetOpen 或 InternetOpenURL WinInet API,而這些都已經為您准備好。托管代碼更為簡單,因為許多 .NET Compact Framework 托管類(如 HttpWeb-Request)已經可以支持代理服務器。

DWORD dwPriority 優先級設置很重要,因為這是連接管理器確定如何共享稀缺連接資源的主要方法。稍後我將對此進行詳細介紹。

BOOL bExclusive 此設置可用來阻止連接管理器與其他應用程序共享網絡連接,甚至是同一個目標元網絡。作為安全緩解,這可能很有用,但使用時應謹慎。

圖 3 使用連接管理器的示例代碼

#include <windows.h>
#include <Connmgr.h>
#include <Wininet.h>
HRESULT ConnectionManager_Connect() {
HRESULT hr = S_OK;
GUID dest;
CONNMGR_CONNECTIONINFO connInfo;
// look up the URL and connect to the specified destination
hr = ConnMgrMapURL(g_urlString, &dest, NULL);
if (SUCCEEDED(hr)) {
memset(&connInfo, 0, sizeof( connInfo));
connInfo.cbSize = sizeof( connInfo );
connInfo.bDisabled = FALSE;
connInfo.bExclusive = FALSE;
connInfo.hWnd = g_hWnd;
connInfo.guidDestNet = dest;
connInfo.dwParams = CONNMGR_PARAM_GUIDDESTNET;
connInfo.dwFlags = CONNMGR_FLAG_PROXY_HTTP;
connInfo.dwPriority = CONNMGR_PRIORITY_USERINTERACTIVE;
connInfo.uMsg = WM_USER+101; // Message callback.
hr = ConnMgrEstablishConnection(&connInfo, &g_ConnHandle);
if (!SUCCEEDED(hr)) {
LogMessage( TEXT("Failed to Establish connection") );
}
}
return hr;
}
HRESULT ProcessConnectionManagerMessage() {
HRESULT hr = S_OK;
DWORD status = 0;
// Connection Manager status change
hr = ConnMgrConnectionStatus( g_ConnHandle, &status);
if (SUCCEEDED(hr)) {
switch (status) {
case CONNMGR_STATUS_CONNECTED:
// Do something useful here…
// Done with the connection—tear it down.
ConnMgrReleaseConnection(g_ConnHandle, 1);
g_ConnHandle = NULL;
break;
case CONNMGR_STATUS_NOPATHTODESTINATION:
case CONNMGR_STATUS_CONNECTIONFAILED:
case CONNMGR_STATUS_CONNECTIONCANCELED:
case CONNMGR_STATUS_CONNECTIONLINKFAILED:
case CONNMGR_STATUS_CONNECTIONDISABLED:
case CONNMGR_STATUS_AUTHENTICATIONFAILED:
// Non transient error state.
ConnMgrReleaseConnection(g_ConnHandle, 1);
g_ConnHandle = NULL;
break;
default:
// These states will eventually end up at as connected
// or not. Just ignore for now.
break;
}
}
return hr;
}
LRESULT CALLBACK WndProc(
HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_USER+101:
ProcessConnectionManagerMessage();
break;
case WM_COMMAND:
if (IDM_GET == LOWORD(wParam)) {
ConnectionManager_Connect();
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

調用 ConnMgrEstablishConnection 可創建一個內部連接請求 (CR),用於跟蹤連接的生命周期。調用 ConnMgrReleaseConnection 時 CR 即釋放。

以下是連接管理器處理 CR 時所遵循的步驟總結。首先,連接管理器驗證是否可以找到連接到目標元網絡的連接路徑。連接管理器檢查 CR,並使用網絡模型(GPRS、WiFi、VPN 或任何其他可用模型,以及代理詳細信息),選擇一組最適合連接到目標元網絡的連接器。

連接管理器不僅局限於一個連接器。例如,為了連接到工作元網絡,可能需要 GPRS 連接和 VPN 連接。連接管理器也可能選擇不同的路徑,具體取決於設備的當前連接狀態。例如,如果設備當前未連接,直接使用 GPRS 連接器可能效果最佳。但是,如果設備當前已連接到工作網絡,則僅使用代理服務器可能就足夠了。如果連接管理器無法找到連接到目標的合適路徑,應用程序會得到通知並且不再繼續執行其他步驟。

接下來,CR 的狀態轉換之旅開始了。每個 CR 都將經歷多種瞬時狀態,才可以最終結束轉換,最終狀態為五種(大約數)關閉狀態之一(連接狀態也為瞬時狀態)。發生狀態更改時,請求應用程序將接收到窗口消息。一旦 CR 到達關閉狀態,便不再更改。因此如果應用程序仍需連接,它應當關閉當前 CR 並創建新的 CR。

一張圖片勝過千言萬語,Adam Dyba 的博客中的連接管理器狀態轉換圖表是我見到過的最棒的說明性圖表。

連接請求和仲裁。

創建了新的 CR,並不意味著它立即就能獲得連接。將執行仲裁:CR 按請求時間進行優先級排序,隨後資源從列表頂部開始向下分配,這樣,最高優先級即最新請求的 CR 先獲得資源,而最低優先級即時間最晚的 CR 最後獲得資源。

不僅新的 CR 會觸發仲裁,更改 CR 優先級、釋放 CR 以及來自網絡資源的事件(例如更改基站)也會觸發仲裁。
語音呼叫也使用連接管理器,還需使用保留的最高優先級標志 CONNMGR_PRIORITY_VOICE 覆蓋所有其他 CR。進行語音連接時,共享語音資源的已連接 CR 可以在語音呼叫期間設置為暫停狀態,然後在語音 CR 釋放後自動重新連接。

網絡復雜性(如 PDP 上下文)可能會對仲裁產生影響。如果僅有一個上下文可用,則只有在列表中處於最高優先級的 CR 會獲得連接,其他 CR 將轉為 WAITINGFORRESOURCE 狀態。這確實有點復雜,因為同一元網絡中任何較低優先級的 CR 還可以根據每個 CR 中的排他設置進入 CONNECTED 狀態。

如果有多個 PDP 上下文,連接管理器將按從高到低的優先級順序連接多個 CR。請務必注意,正如前面所述,新的 CR 始終優先於具有相同優先級的現有 CR。

就此考慮片刻 — 這有一個煩人的陷阱,粗心的開發人員非常容易掉進去。假設,我有一個始終連接的應用程序,並且該應用程序不需要通過 Internet 訪問即可連接到專用 APN。我的應用程序正在後台運行,並且 CR 已連接,因此數據可以順暢傳送到服務器,也可以順暢從服務器返回數據。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved