本文章是由機器翻譯。

Windows 8 網路功能

Windows 8 與 WebSocket 通訊協定

Kenny Kerr

 

WebSocket 協定的目的是提供一個主導的獨自負責建立連接和發起請求/回應對用戶端的 Web 飽和的世界中的雙向通信。 最後,它允許應用程式享有更多的好處的 TCP,但在 Web 友好的方式。 考慮到 2011 年 12 月,WebSocket 協定只標準化互聯網工程任務組 — — 當我寫這是仍在審議由全球資訊網協會 — — 它也許出人意料的只是如何全面 Windows 8 已接受了這種新的互聯網技術。

在這篇文章我先向您展示 WebSocket 協定的原理和解釋其關係到較大的 TCP/IP 套件。 然後,我將探討程式師能夠輕鬆地採用這項新技術從其應用程式中的可以使用的 Windows 8 的各種方法。

為什麼 WebSocket 嗎?

本議定書的主要目標是提供一種基於瀏覽器的應用程式與自由外請求/回應對伺服器進行通信的標準和有效的方法。 在幾年以前,Web 開發人員是所有飄淺談非同步 JavaScript 和 XML (AJAX) 以及它如何使動態和互動式的方案 — — 和肯定它了,但用戶端物件啟發它所有仍然只允許瀏覽器發出 HTTP 要求。 如果伺服器要將消息發送到用戶端帶外的嗎? 這就是 WebSocket 議定書 》 是哪裡來。 它不僅允許伺服器將郵件發送到用戶端,但是,它無需開銷 HTTP,提供雙向通信,這就是原始的 TCP 連接的速度。 如果 WebSocket 議定書,Web 開發人員不得不通過輪詢伺服器的更新,濫用 HTTP 沒有使用彗星式程式設計技術,並雇用了大量的協定開銷只是與許多 HTTP 連接來使應用程式保持最新。 伺服器負載過重、 浪費頻寬和 Web 應用程式過於複雜。 WebSocket 議定書中出奇簡單而高效的方式,解決這些問題,但我可以描述它是如何工作之前,我需要提供一些基礎性和歷史背景。

TCP/IP 套件

TCP/IP 是一個協定套件或相互關聯的協定,實現互聯網架構的集合。 多年來它已經成為其當前的表單。 自從 1960 年代,當資料包切換式網路的概念首先發展,世界已經大大改變。 電腦已經變得更快、 軟體增加要求更高,互聯網已爆出包羅萬象的資訊、 通信和互動,web 和今天流行的使用中是骨幹的這麼多的軟體。

Tcp/ip 包括鬆散仿照的開放系統互相連線 (OSI) 分層模型的層數。 雖然不特別好劃定的不同圖層上的協定,TCP/IP 已清楚地證明其有效性,和分層問題已經被解決了硬體和軟體設計的巧妙組合。 將 TCP/IP 分離到圖層,但是含糊不清它們可能,説明了它的發展隨著時間的推移,隨著硬體和技術發生了變化,並已允許程式師具有不同技能不同級別的抽象,要麼説明打造的協定棧本身或生成應用程式的工作及其各項設施的利用。

在的最低層是一個物理的協定,包括有線的媒體存取控制和 Wi-Fi,提供物理連接,以及本地的處理和錯誤偵測的喜歡。 大多數程式師不要想太多關於這些協定。

在堆疊中向上移動,互聯網協定 (IP) 本身駐留在網路層中,並允許 TCP/IP 成為可交互操作的跨越不同的實體層。 它負責的電腦位址映射到物理位址和路由從電腦到電腦的資料包。

然後有附屬協定,而我們可以辯論有關哪一層它們駐留在但他們真的為自動設定、 名稱解析、 發現、 路由優化和診斷提供了必要的支援作用。

我們將進一步上移的分層堆疊、 傳輸和應用協定來入視圖。 傳輸協議照顧多工和多工從較低的層的資料包,這樣即使有可能只是單一的物理和網路層,許多不同的應用程式可以共用的通信通道。 傳輸層通常還提供了進一步的錯誤偵測、 可靠傳遞和甚至與性能相關的功能,例如擁塞和流量控制。 應用程式層歷來的協定 (如 HTTP (由 Web 瀏覽器和伺服器實現) 和 SMTP (由電子郵件用戶端和伺服器實現) 家。 世界已開始更加依賴協定 (如 HTTP、 它們的實現已被推入作業系統,兩者,來改善化以及共用不同應用程式之間執行的深處。

TCP 和 HTTP

在 tcp/ip 協定,TCP 和使用者資料包協定 (UDP) 發現在傳輸層是也許最熟知的一般的程式師。 都定義了一個"埠"抽象,這些協定使用與 IP 位址結合多重和中分離的資料包到達時,當它們被發送。

雖然 UDP 頻繁用於其他 TCP/IP 協定 (如動態主機設定通訊協定和 DNS,並已廣泛採用專用的網路應用程式,它通過在互聯網上在逃沒有作為其一個同級的深遠。 TCP,另一方面,已廣泛採用一刀切,謝謝在看到到 HTTP 的很大一部分。 雖然 TCP UDP 比複雜得多,但很多這種複雜性是隱藏的應用程式而不受其複雜性享有的優勢是 TCP 的位置的應用程式層。

TCP 提供可靠的資訊流的執行是極其複雜的電腦之間的資料。 它涉及本身與資料包排序和資料重建、 錯誤偵測和恢復、 擁塞控制和性能、 超時、 重傳和更多。 該應用程式,但是,只看到港口之間的雙向連接,並假定發送和接收的資料將傳輸正確,順序。

前提是可靠的連線導向的協定,當代 HTTP 和 TCP 顯然是明顯和無處不在的選擇。 在此模型中,HTTP 函數作為用戶端-伺服器協定。 用戶端打開到伺服器的 TCP 連接。 然後,它發送一個請求,伺服器的計算結果和回應的。 這被重複的每一秒每一天,世界各地的無數倍。

當然,這是功能的一種精簡或限制 TCP 提供。 TCP 允許雙方要同時發送資料。 一端不需要等待,另一個用於發送請求之前它能夠作出反應。 這種簡化了,但是,允許伺服器端緩存的回應,其中上規模的網站的能力產生了巨大影響。 但 HTTP 的普及無疑輔助由其初始的簡單性。 而 TCP 為二進位資料提供了一個雙向通道 — — 一對的流,如果你喜歡 — — HTTP 提供了請求消息之前回應訊息,兩者組成的 ASCII 字元,雖然郵件的正文中,如果任何,可能進行編碼以某種其他方式。 一個簡單的請求可能如下所示:

GET /resource HTTP/1.1\r\n
host: example.com\r\n
\r\n

每行最後一個回車符 (\r) 和分行符號 (\n)。 第一行中,稱為請求行中,指定資源的可訪問 (在本例中獲取),資源的路徑和最後要使用的 HTTP 版本的方法。 類似于低層協定,HTTP 提供多工和多工透過此資源路徑。 此請求行如下一個或多個標題列。 標頭包含名稱和值,如前面的示例中所示。 某些頭是必需的如主機,大部分並不是只是協助瀏覽器和伺服器中更有效地進行通信,或談判的特性和功能。

回應可能類似如下所示:

HTTP/1.1 200 OK\r\n
content-type: text/html\r\n
content-length: 1307\r\n
\r\n
<!DOCTYPE HTML><html> ...
</html>

格式基本上是相同的但而不是請求行,回應行申明的版本要使用的 HTTP 狀態碼 (200)、 狀態碼的描述。 已成功處理請求和任何結果是包含任何標題列後立即將指示的 200 狀態碼到用戶端。 例如,伺服器可能,指示所請求的資源並不存在通過返回 404 狀態碼。 標頭採取那些請求中相同的形式。 在這種情況下的內容類型標頭通知的瀏覽器所請求的資源,在郵件正文中是要被解釋為 HTML 和內容長度標頭告訴瀏覽器郵件正文中包含的位元組數。 這是重要的因為你會記得,HTTP 消息流過 TCP,而不能提供的消息邊界。 沒有內容的長度,HTTP 應用程式需要使用各種試探法來確定任何郵件內文的長度。

這是所有相當簡單,簡單設計的 HTTP 的見證。 但 HTTP 不再簡單。 今天的 Web 瀏覽器和伺服器是成千上萬的相互關聯的功能,與先進的程式和 HTTP 是需要跟上它所有的主力。 很多複雜性誕生極品飛車。 現在有標頭進行談判的郵件內文、 緩存和過期的標頭,以避免在所有傳輸郵件正文和更多的壓縮。 已開發技術,結合不同的資源減少 HTTP 要求的數量。 內容傳遞網路 (Cdn) 甚至已分發到世界各地在嘗試向通常訪問主機資源更接近訪問他們的 Web 瀏覽器中。

儘管所有這些進展,許多 Web 應用程式能實現更大的可擴充性和甚至簡單起見,如果有一些偶爾中斷 HTTP 並返回到的 TCP 流模型的方法。 這正是 WebSocket 議定書 》 所提供的了。

WebSocket 握手

WebSocket 議定書有點整齊地裝入 tcp/ip 上方 TCP 和 HTTP 旁邊。 與新協定引入互聯網的挑戰之一就是在某種程度上使無數的路由器、 代理伺服器和防火牆認為一切都沒有改變在陽光下。 WebSocket 協定切換到同一基礎 TCP 連接上自己 WebSocket 資料傳輸之前偽裝成 HTTP 實現這一目標。 這種方式,許多輕信仲介不需要升級,以便允許 WebSocket 通訊遍歷其網路連接。 在實踐中這不會總是工作所以進行得很順利,因為某些過於熱衷的路由器撥弄的 HTTP 要求和回應,試圖重寫它們適合他們自己的目的,如代理伺服器緩存或處理或資源翻譯。 在短期內有效的解決方案是使用通過安全通道的 WebSocket 協定 — 傳輸層安全性 (TLS) — — 因為這往往保持到最低篡改。

WebSocket 協定借從各種各樣的來源,包括 IP、 UDP、 TCP 和 HTTP,想法,並使這些概念可用到 Web 瀏覽器和其他應用程式中較簡單的形式。 這一切都開始設計了來看,就像一個 HTTP 要求/回應對運作的握手。 這並不是這樣,用戶端或伺服器可以以某種方式欺騙對方使用 WebSockets,但而是要愚弄的各種仲介到思考它是服侍 HTTP 的只是另一個 TCP 連接。 事實上,WebSocket 議定書 》 專門用來防止任何一方被騙而意外地接受連接。 它開始與用戶端發送一次握手,為所有意圖和目的,是 HTTP 要求,並且可能如下所示:

GET /resource HTTP/1.1\r\n
host: example.com\r\n
upgrade: websocket\r\n
connection: upgrade\r\n
sec-websocket-version: 13\r\n
sec-websocket-key: E4WSEcseoWr4csPLS2QJHA==\r\n
\r\n

正如您所看到的並不排除這從正在一個完全有效的 HTTP 要求。 毫無戒心的仲介只是應將沿此請求傳遞到伺服器,其中甚至可能會增加一倍為 WebSocket 伺服器的 HTTP 伺服器。 在此示例中的請求行指定一個標準的 GET 請求。 這也意味著,WebSocket 伺服器可能允許多個終結點,由單個伺服器中大多數 HTTP 伺服器的相同方式提供服務。 主機頭所需的 HTTP 1.1 和為了同樣的目的 — — 以確保雙方同意在宿主域共用宿主方案。 升級和連接頭也是由用戶端用來請求在連接中使用的協定升級的標準 HTTP 標頭。 雖然這是罕見,過渡到一個安全的 TLS 連接,HTTP 用戶端有時會使用這種技術。 這些標頭檔,但是,協定所需的 WebSocket。 具體來說,升級的頭指示連接應升級到 WebSocket 議定書,並連接頭指定此升級的頭是特定連接,也就是說它必須不通報由代理伺服器通過進一步連接。

Sec-websocket-版本標頭必須被包括在內,其值必須是 13。 如果伺服器是 WebSocket 的伺服器,但不支援此版本,它將會中止握手,返回適當的 HTTP 狀態碼。 您將看到一會兒,即使伺服器一無所知的 WebSocket 議定書和愉快地返回一個成功的回應,用戶端被為了中止連接。

Sec-websocket-鍵頭真的是 WebSocket 握手的關鍵。 WebSocket 協定的設計者想要確保伺服器可能不能接受從事實上不是 WebSocket 用戶端的用戶端的連接。 他們不想讓惡意腳本來構建表單提交或使用用戶端物件來假 WebSocket 連接通過添加 sec-* 標頭。 為了證明雙方正在建立的合法連接,sec-websocket-鍵頭還必須存在於用戶端的握手。 值必須是隨機播放 — — 最理想的是密碼隨機 — — 16 位元組數,稱為 nonce 在安全用語,然後是 base64-­編碼為此標頭值。

一旦發送用戶端握手時,用戶端等待回應以驗證服務器確實願意並能夠建立 WebSocket 連接。 假定伺服器不反對,它可能會如下作為 HTTP 回應發送伺服器握手:

HTTP/1.1 101 OK
upgrade: websocket\r\n
connection: upgrade\r\n
sec-websocket-accept: 7eQChgCtQMnVILefJAO6dK5JwPc=\r\n
\r\n

再次,這是完全有效的 HTTP 回應。 回應行包括隨後的狀態碼,但不經常的 HTTP 版本 200 代碼指示成功,伺服器必須回應與指示伺服器瞭解升級請求,並且願意切換協定的標準 101 代碼。 英語的狀態碼的描述都絕對沒有區別。 它可能是"OK"或"切換到 WebSocket"或甚至是一個隨機的與馬克 · 吐溫的報價。 重要的是狀態碼和用戶端必須確保它是 101。 例如,伺服器無法拒絕的請求,並要求用戶端使用一個 401 狀態碼在接受 WebSocket 用戶端握手之前進行身份驗證。 然而,成功的回應必須包括的升級和連接的標頭,要承認的 101 狀態碼特指切換到 WebSocket 的議定書 》,再次以避免任何人受騙。

最後,若要驗證握手,用戶端確保 sec-websocket 接受標頭是目前在回應中,其值正確。 伺服器不必解碼的用戶端發送的 base64 編碼的值。 它只是將此字串、 串聯一個知名的 GUID 的字串表示形式和 sha-1 演算法產生一個 20 位元組的值,然後是 base64 編碼和使用作為 sec-websocket 接受標頭的值的組合進行雜湊處理。 用戶端可以然後輕鬆地驗證服務器的確做的要求,然後毫無疑問的是,雙方都同意的 WebSocket 連接。

如果一切順利,在此時建立一個有效的 WebSocket 連接和雙方可以進行自由和同時在兩個方向上使用 WebSocket 資料幀中。 很顯然從學習 WebSocket 議定書 》,它設計後的 Web 的不安全啟示。 不同于其前任的大多數,WebSocket 協定旨在與安全意識。 議定書 》 還要求用戶端包括原產地標頭,如果用戶端事實上是一個 Web 瀏覽器。 這將允許瀏覽器來對跨起源攻擊提供保護。 當然,這才有意義的受信任的主控環境,這樣瀏覽器的上下文中。

WebSocket 資料傳輸

WebSocket 議定書 》 是所有關于獲取 Web 回通信提供的 IP 和 TCP,不增加複雜性和開銷的進一步層的較高性能、 低開銷模型。 為此,一旦握手完成,WebSocket 開銷減至最低。 它提供了資料包-­框架機制在 TCP IP 分幀的讓人想起該 TCP 本身建立在和 UDP 是如此受歡迎,但不具有哪些那些資料包大小限制的情況下擔保協定。 而 TCP 提供基於流的抽象,WebSocket 提供了對應用程式是基於消息的抽象。 雖然 TCP 流傳輸通過線段,WebSocket 消息運送作為幀序列。 這些幀通過同一個 TCP 連接傳輸,因此自然以為可靠和順序傳遞。 框架協定是有點複雜,但專門用於將極小,需要在很多情況下只有幾個額外的位元組的幀開銷。 開幕握手完成後的任何時候,可能由用戶端或伺服器傳輸資料幀。

每個幀包括描述框架類型作為好的負載大小的操作碼。 這個有效載荷表示應用程式可能需要進行通信的實際資料,以及任何預先安排好的擴展資料。 有趣的是,議定書 》 允許支離破碎的消息。 如果你來自一個鐵杆的網路背景,您可能會提醒的 IP 級碎片和痛苦的 TCP 去避免碎片的性能影響。 但碎片的 WebSocket 概念是相當不同。 這裡的想法是讓 WebSocket 協定提供方便快捷的網路資料包,但沒有大小限制。 如果寄件者並不知道的正在發送的消息的確切長度,它可能會分散,每個幀都表明有多少資料,它提供了和它是不是最後一個片段。 除此之外,該框架只是表示它是否包含二進位資料或 UTF-8 編碼的文本。

控制框架也定義和主要用來關閉連接,但也可用作為一個心跳來 ping 另一個端點以確保它仍然作出反應或協助讓活著的 TCP 連接。 最後,我要指出,如果你碰巧捅在使用網路通訊協定分析器 Wireshark 等用戶端發送的 WebSocket 幀,您可能會注意到資料幀似乎包含已編碼的資料。 WebSocket 議定書要求從用戶端發送到伺服器的所有資料幀會被都遮罩。 掩蔽與掩蔽鍵涉及一個簡單的演算法"XOR'ing"的資料位元組數。 所以這並不為了成為某種可笑的安全功能,雖然它不是關乎安全掩蔽的關鍵被載在機架內。 如前所述,WebSocket 協定的設計者們花了大量的工作經歷與安全有關的各種方案的努力嘗試去預測中,議定書 》 可能會受到攻擊的各種方式。 一個分析了這種攻擊媒介參與攻擊 WebSocket 議定書間接的損害互聯網的基礎設施,在此案例代理伺服器中的其他部分。 毫無戒心,可能不知道的 WebSocket 握手神似的 GET 請求的代理伺服器可能受騙,假 GET 請求發起的攻擊,實際上中毒某些使用者的緩存的資料緩存。 用新金鑰掩蔽的每個幀確保幀不是可預測,並因此不能被誤解線上緩解此特定的威脅。 這種攻擊,頗有點更多是和研究者們無疑會發現一些時間進一步可利用的漏洞。 仍然,它是攻擊的令人印象深刻,看到設計師已經嘗試預見的許多形式的長度。

Windows 8 與 WebSocket 通訊協定

它是有深刻的瞭解 WebSocket 議定書 》 有用,它還説明了大量具有這種廣泛的支援,平臺上工作,Windows 8 當然提供了。 讓我們看看一些在其中您可以使用 WebSocket 協定,而實際上不必自己實現議定書 》 的方法。

Windows 8 提供了 Microsoft.NET 框架,支援用戶端通過 Windows 運行時的本機和託管代碼,您可以創建使用 Windows HTTP 服務 (WinHTTP) c + + 中的 API 的 WebSocket 用戶端。 最後,IIS 8 提供了本機的 WebSocket 模組,和 Internet Explorer 當然為 WebSocket 協定提供本機支援。 這是混合不同的環境,但可能是什麼更令人驚訝是 Windows 8 只包括單一的 WebSocket 執行,所有這些之間共用。 WebSocket 協定元件 API 實現握手和構圖的協定規則的所有沒有過實際創建任何類型的網路連接。 不同的平臺和運行時環境可以然後使用此共同執行,並將其掛鉤到他們選擇的網路堆疊。

.NET 用戶端和伺服器

.NET 框架提供的擴展對 ASP.NET 並提供可以 — — 基於本機 HTTP 伺服器 IIS 使用的 API 的本身也是 — — 為 WebSocket 議定書 》 提供伺服器支援。 在 ASP.NET 中,您可以簡單地編寫調用新的 HttpCoNtext.AcceptWebSocketRequest 方法,來接受特定終結點上的 WebSocket 請求的 HTTP 處理常式。 您可以驗證該請求是使用 HttpCoNtext.IsWebSocketRequest 屬性的 WebSocket 用戶端握手。 在 ASP.NET 外你可以通過簡單地使用可以類承載 WebSocket 的伺服器。 執行也大多是兩者之間共用的。 圖 1 提供了一個簡單的例子,這種伺服器。

圖 1 WebSocket 伺服器使用可以

static async Task Run()
{
  HttpListener s = new HttpListener();
  s.Prefixes.Add("http://localhost:8000/ws/");
  s.Start();
  var hc = await s.GetContextAsync();
  if (!hc.Request.IsWebSocketRequest)
  {
    hc.Response.StatusCode = 400;
    hc.Response.Close();
    return;
  }
  var wsc = await hc.AcceptWebSocketAsync(null);
  var ws = wsc.WebSocket;
  for (int i = 0; i != 10; ++i)
  {
    await Task.Delay(2000);
    var time = DateTime.Now.ToLongTimeString();
    var buffer = Encoding.UTF8.GetBytes(time);
    var segment = new ArraySegment<byte>(buffer);
    await ws.SendAsync(segment, WebSocketMessageType.Text,
      true, CancellationToken.None);
  }
  await ws.CloseAsync(WebSocketCloseStatus.NormalClosure,
    "Done", CancellationToken.None);
}

我在這裡使用 C# 非同步方法以保持代碼的順序和連貫,但事實上,它是所有非同步。 我開始通過註冊終結點並等待傳入的請求。 然後檢查請求是否不會其實有資格作為 WebSocket 握手並且返回 400"錯誤的請求"的狀態碼,如果它不是。 我然後調用 AcceptWebSocketAsync,接受用戶端握手並等待握手來完成。 在這一點上,我可以自由地交流使用的 WebSocket 物件。 在此示例中,伺服器發送幀,UTF 8 月 10 日每個包含一個短暫的延遲後的時間。 每個幀發送使用非同步 SendAsync 方法。 此方法是功能非常強大,可以發送 utf-8 或二進位幀作為一個整體或片段中。 第三個參數 — — 在這種情況下,真正的 — — 指示是否對 SendAsync 此調用表示消息的結尾。 因此,您可以使用此方法一再為您發送很長的郵件,會碎片。 最後,CloseAsync 方法用於執行清潔封閉的 WebSocket 連接,發送一個密切控制幀和等待用戶端要承認有其自己的關閉幀。

在用戶端上的新的 ClientWebSocket 類區域性物件內部使用以提供連接到 WebSocket 伺服器的能力。 圖 2 提供了一個簡單的例子,可以用來連接到的伺服器的用戶端圖 1

圖 2 使用 ClientWebSocket 的 WebSocket 用戶端

static async Task Client()
{
  ClientWebSocket ws = new ClientWebSocket();
  var uri = new Uri("ws://localhost:8000/ws/");
  await ws.ConnectAsync(uri, CancellationToken.None);
  var buffer = new byte[1024];
  while (true)
  {
    var segment = new ArraySegment<byte>(buffer);
    var result =
      await ws.ReceiveAsync(segment, CancellationToken.None);
    if (result.MessageType == WebSocketMessageType.Close)
    {
      await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "OK",
        CancellationToken.None);
      return;
    }
    if (result.MessageType == WebSocketMessageType.Binary)
    {
      await ws.CloseAsync(WebSocketCloseStatus.InvalidMessageType,
        "I don't do binary", CancellationToken.None);
      return;
    }
    int count = result.Count;
    while (!result.EndOfMessage)
    {
      if (count >= buffer.Length)
      {
        await ws.CloseAsync(WebSocketCloseStatus.InvalidPayloadData,
          "That's too long", CancellationToken.None);
        return;
      }
      segment =
        new ArraySegment<byte>(buffer, count, buffer.Length - count);
      result = await ws.ReceiveAsync(segment, CancellationToken.None);
      count += result.Count;
    }
    var message = Encoding.UTF8.GetString(buffer, 0, count);
    Console.WriteLine("> " + message);
  }
}

在這裡我使用 ConnectAsync 方法來建立連接並執行 WebSocket 握手。 請注意該 URL 使用的新的"ws"URI 方案標識此作為 WebSocket 終結點。 隨著 HTTP,ws 的預設埠為埠 80。 "Wss"計畫也被定義為表示 TLS 的安全連線,並使用相應的埠 443。 用戶端然後調用 ReceiveAsync 在一個迴圈來接收伺服器是願意發送多幀中。 一旦收到,框架首先檢查以查看它是否表示密切控制幀。 在這種情況下用戶端回應通過發送自己密切的框架,允許伺服器以迅速關閉連接。 用戶端然後檢查是否該框架包含二進位資料,這種情況下與一個錯誤,指示此框架類型不受支援,關閉連接。 最後,可以讀取的幀資料。 為了適應零碎的消息,一種 while 迴圈等待直到收到的最後片段。 新的 ArraySegment 結構用於管理抵消所以碎片重組正確的緩衝區。

WinRT 用戶端

WebSocket 議定書的 Windows 運行時支援是有點更具限制性的。 只有用戶端都支援,並且他們可以讀取之前,必須完全緩衝零碎的 UTF 8 消息。 二進位消息僅可以使用此 API 進行流式處理。 圖 3 提供了一個簡單的示例,還可以用來連接到的伺服器的用戶端圖 1

圖 3 WebSocket 用戶端使用 Windows 運行時

static async Task Client()
{
  MessageWebSocket ws = new MessageWebSocket();
  ws.Control.MessageType = SocketMessageType.Utf8;
  ws.MessageReceived += (sender, args) =>
  {
    var reader = args.GetDataReader();
    var message = reader.ReadString(reader.UnconsumedBufferLength);
    Debug.WriteLine(message);
  };
  ws.Closed += (sender, args) =>
  {
    ws.Dispose();
  };
  var uri = new Uri("ws://localhost:8000/ws/");
  await ws.ConnectAsync(uri);
}

此示例中,雖然也寫在 C# 中,依靠事件處理常式在最大程度上,和 C# 非同步方法是用處不大,只能夠允許 MessageWebSocket 物件非同步連接。 代碼是相當簡單,但是,如果有點古怪。 一旦整個 (可能是零碎) 郵件已收到並準備讀取調用的 MessageReceived 事件處理常式。 即使已收到整個的消息,它只能是 utf-8 字串,它存儲在一個流,並必須使用 DataReader 物件讀取內容並返回一個字串。 最後,關閉的事件處理常式可以讓你知道伺服器發送的密切控制幀,但如與.NET ClientWebSocket 類,您仍然負責將密切控制幀發送回伺服器。 然而,MessageWebSocket 類,只發送此幀的物件是本身被摧毀之前,只是一些。 若要使這種情況發生在 C# 中的迅速,需要調用 Dispose 方法。

在典型的 JavaScript 用戶端

有什麼疑問的 JavaScript 是 WebSocket 議定書將作出最大的影響,環境和 API 是令人印象深刻的簡單。 這裡是它用來連接到該伺服器的圖 1

var ws = new WebSocket("ws://localhost:8000/ws/");
ws.onmessage = function (args)
{
  var time = args.data;
  ...
};

與在 Windows 上的其他 Api,不同瀏覽器照顧的自動關閉 WebSocket 連接,當它收到一個密切控制幀。 可以,當然,顯式關閉連接或處理 onclose 事件,但沒有採取進一步的行動在您完成所需閉幕握手。

WinHTTP c + + 用戶端

當然,WinRT WebSocket 用戶端 API 可以從本機 c + + 以及,使用,但如果你在尋找更多的控制,然後 WinHTTP 是你的東西。 圖 4 提供了一個簡單的例子,使用 WinHTTP 要連接到的伺服器的圖 1。 此示例為了簡明起見,在同步模式下使用 WinHTTP API,但這會同樣也以非同步方式工作。

圖 4 使用 WinHTTP 的 WebSocket 用戶端

auto s = WinHttpOpen( ...
);
auto c = WinHttpConnect(s, L"localhost", 8000, 0);
auto r = WinHttpOpenRequest(c, nullptr, L"/ws/", ...
);
WinHttpSetOption(r, WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET, nullptr, 0);
WinHttpSendRequest(r, ...
);
VERIFY(WinHttpReceiveResponse(r, nullptr));
DWORD status;
DWORD size = sizeof(DWORD);
WinHttpQueryHeaders(r,
  WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
  WINHTTP_HEADER_NAME_BY_INDEX,
  &status,
  &size,
  WINHTTP_NO_HEADER_INDEX);
ASSERT(HTTP_STATUS_SWITCH_PROTOCOLS == status);
auto ws = WinHttpWebSocketCompleteUpgrade(r, 0);
char buffer[1024];
DWORD count;
WINHTTP_WEB_SOCKET_BUFFER_TYPE type;
while (NO_ERROR ==
  WinHttpWebSocketReceive(ws, buffer, sizeof(buffer), &count, &type))
{
  if (WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE == type)
  {
    WinHttpWebSocketClose(
      ws, WINHTTP_WEB_SOCKET_SUCCESS_CLOSE_STATUS, nullptr, 0);
    break;
  }
  if (WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE == type ||
    WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE == type)
  {
    WinHttpWebSocketClose(
      ws, WINHTTP_WEB_SOCKET_INVALID_DATA_TYPE_CLOSE_STATUS, nullptr, 0);
    break;
  }
  std::string message(buffer, count);
  while (WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE == type)
  {
    WinHttpWebSocketReceive(ws, buffer, sizeof(buffer), &count, &type);
    message.append(buffer, count);
  }
  printf("> %s\n", message.c_str());
}

正如與 WinHTTP 的所有用戶端,您需要創建 WinHTTP 會話、 連接和請求的物件。沒有什麼新在這裡所以我已經省略了一些詳細資訊。其實在發送請求之前, 您需要設置新 WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET 選項上指示 WinHTTP 的請求,執行 WebSocket 握手。然後準備將函數與發送請求。然後使用週期性 WinHttpReceiveResponse 函數等待回應,從而在這種情況下將包括 WebSocket 握手的結果。一如既往,以確定請求的結果,WinHttpQueryHeaders 函數被調用具體要讀取從伺服器返回的狀態碼。在這一點上,已建立 WebSocket 連接,您可以開始使用它直接。WinHTTP API 自然處理你,構圖,此功能通過一個新的 WinHTTP WebSocket 物件,通過在請求物件上調用 WinHttpWebSocketCompleteUpgrade 函數檢索公開。

從伺服器接收的消息做,至少在概念上,在作為示例中一樣圖 2。WinHttpWebSocketReceive 函數等待接收下一個資料幀。它還允許您讀取任何種類的 WebSocket 消息和中的示例片段圖 4 闡釋這如何可能做在一個迴圈中。如果收到的密切控制幀,然後匹配的密切幀被發送到伺服器,使用 WinHttpWebSocketClose 函數。如果收到了二進位資料幀,然後同樣關閉連接。請記住這只會關閉 WebSocket 連接。您仍然需要調用 WinHttpCloseHandle 來釋放 WinHTTP WebSocket 物件,如你要做 WinHTTP 您擁有的所有物件。如我所述我 2011 年 7 月的專欄,"c + + 和 Windows API"一個控制碼的包裝類 (msdn.microsoft.com/magazine/hh288076),將做訣竅。

WebSocket 協定是一項重大的新創新,在 Web 應用程式的世界,和其相對簡單,儘管是較大的 TCP/IP 協定套件的歡迎加入。我毫不懷疑 WebSocket 議定書不久將幾乎與 HTTP 本身,輔助應用程式一樣的無處不在和系統的各種溝通更輕鬆高效地連接。Windows 8 做了自己的作用,為構建 WebSocket 用戶端和伺服器提供一套全面的 Api。

Kenny Kerr 是充滿熱情的本機 Windows 開發的軟體工匠。他在聯繫 kennykerr.ca

感謝以下技術專家對本文的審閱:皮奧特爾 · 庫瓦加和亨利 · 查理斯 Machalani