2. 何謂 Socket

你一直聽到人家在講"sockets",你可能也想知道這些是什麼東西。

好的,其實它們是:利用標準 UNIX file descriptors(檔案描述子)與其它程式溝通的一種方式。

什麼?

OK,你可能有聽過有些駭客(hacker)說過:"我的天呀!在 UNIX 系統中的任何東西都可以視為檔案!"

他說的是真的,不是假的。當 UNIX 程式要做任何類型的 I/O 時,它們會讀寫 file descriptor。File descriptor 單純只是跟已開啟檔案有關的整數。只是[關鍵在於],該檔案可以是一個網路連線、FIFO、pipe(管線)、terminal(終端機)、真實的磁碟檔案、或只是相關的東西。在 UNIX 所見都是檔案!所以當你想要透過 Internet(網際網路)跟其它的程式溝通時,你需要透過一個 file descriptor 來達成,這點你一定要相信。

"那麼,Smarty-Pants 先生,我在哪裡可以取得這個用在網路通信的 file descriptor 呢?"

這可能是你現在心裡的問題,我會跟你說的:你能呼叫 socket() system routine(系統常式)。它會傳回 socket descriptor,你可以用精心設計的 send() 與 recv() socket calls[man sendman recv]來透過 socket descriptor 進行通信。

"不過,嘿嘿!"

現在你可能在想:"既然只是個 file descriptor,為什麼我不能用一般的 read() 與 write() call 透過 socket 進行通信,而要用這什麼鬼東西?"

簡單說:"可以!"

詳細點的說法是:"可以,不過 send() 與 recv() 讓你能對資料傳輸有更多的控制權"。

接下來呢?

這麼說吧:有很多種 sockets,如 DARPA Internet Sockets(網際網路位址)、本機端上的路徑名稱(path names on a local node,UNIX Sockets)、CCITT X.25 位址(你可以放心忽略 X.25 Sockets),可能還有其它的,要看你用的是哪種 UNIX 系統。在這裡我們只討論第一種:Internet Sockets。

2.1. 兩種 Internet Sockets

這是什麼?有兩種 Internet sockets 嗎?

是的,喔不,我騙你的啦。其實有更多種 Internet sockets,只是我不想嚇到你。所以這裡我只打算討論兩種,不過我還會告訴你"Raw Sockets",這是很強大的東西,所以你應該要好好研究一下它們。

譯註:
一般的 socket 只能讀取傳輸層以上[不含]的資訊,raw socket 一般用在設計 network sniffer,可以讓應用程式取得網路封包底層的資訊[如 TCP 層、IP 層,甚至 link layer socket 可以讀取到 link layer 層],並用以分析封包資訊。這份文件不會談到這類的程式設計,有興趣的讀者可自行參考:Unix Network Programming Vol. 1TCP/IP 網路程式實驗與設計 libpcap

好吧,不聊了。那到底是有哪兩種 Internet sockets 呢?

其中一個是"Stream Sockets"(串流式 Sockets);而另一個是"Datagram Sockets"(訊息式 Sockets),之後我們分別以"SOCK_STREAM"與"SOCK_DGRAM"來表示。Datagram sockets 有時稱為"免連線的 sockets"(connectionless sockets)(雖然它們也可以用 connect(),如果你想這麼做的話,請見後面章節的 connect())。

Stream sockets 是可靠的、雙向連接的通信串流。若你以"1、2"的順序將兩個項目輸出到 socket,它們在另一端則會以"1、2"的順序抵達。而且不會出錯。

哪裡會用到 stream sockets 呢?

好的,你應該聽過 telnet 程式吧,不是嗎?它就是用 stream sockets。你所輸入的每個字都需要按照你所輸入的順序抵達,有嗎?網站瀏覽器所使用的 HTTP 通訊協定也是用 stream sockets 取得網頁。的確,若你以 port 80 telnet 到一個網站,並輸入"GET / HTTP/1.0",然後按兩下 Enter,它就會輸出 HTML 給你!

Stream sockets 是如何達成如此高品質的資料傳送呢?

它們用所謂的"The Transmission Control Protocol"(傳輸控制協定),就是常見的"TCP"(TCP 的全部細節請參考RFC 793[6])。TCP 確保你的資料可以依序抵達而且不會出錯。你以前可能聽過"TCP"是"TCP/IP"比較優的部分,這邊的"IP"是指"Internet Protocol"(網際網路協定,請見 RFC 791[7])。IP 主要處理 Internet routing(網際網路的路由遶送),通常不保障資料的完整性。

酷喔。那 Datagram socket 呢?為什麼它們號稱免連線呢?這邊有什麼好主意?為什麼它們是不可靠的?

好,這裡說明一下現況:如果你送出一個 datagram(訊息封包),它可能會順利到達、可能不會按照順序到達,而如果它到達了,封包中的資料就是正確的。

譯註:
TCP 會在傳輸層對將上層送來的過大訊息分割成多個分段(TCP segments),而 UDP 本身不會,UDP 是訊息導向的(message oriented),若 UDP 訊息過大時(整體封包長度超過 MTU),則會由 host 或 router 在 IP 層對封包進行分割,將一個 IP packet 分割成多個 IP fragments。IP fragmention 的缺點是,接收端的系統需要做 IP 封包的重組,將多個 fragments 重組合併為原本的 IP 封包,同時也會增加封包遺失的機率。如將一個 IP packet 分裂成多個 IP fragments,只要其中一個 IP fragment 遺失了,接收端就會無法順利重組 IP 封包,因而造成封包的遺失,若是高可靠度的應用,則上層協定需重送整個 packet 的資料。

[6] http://tools.ietf.org/html/rfc793
[7] http://tools.ietf.org/html/rfc791

Datagram sockets 也使用 IP 進行 routing(路由遶送),不過它們不用 TCP;而是用"UDP,User Datagram Protocol"(使用者資料包協定,請見 RFC 768 [8])。

為什麼它們是免連線的?

好,基本上,這跟你在使用 stream socket 時不同,你不用維護一個開啟的連線,你只需打造封包、給它一個 IP header 與目的資訊、送出,不需要連線。通常用 datagram socket 的時機是在沒有可用的 TCP stack 時;或者當一些封包遺失不會造成什麼重大事故時。這類應用程式的例子有:tftp(trivial file transfer protocol,簡易檔案傳輸協定,是 FTP 的小兄弟),多人遊戲、串流音樂、影像會議等。

"等一下!tftp 和 dhcpd 是用來在一台主機與另一台之間傳輸二進制的應用資料!你如果想要應用程式能在資料抵達時正常運作,那資料就不能遺失阿!這是什麼黑魔法?"

好,我的人族好友,tftp 與類似的程式會在 UDP 的上層使用它們自己的協定。比如:tftp 協定會報告每個收送的封包,接收端必須送回一個封包表示:"我收到了!"[一個"ACK"回報封包]。若原本封包的傳送端在五秒內沒有收到回應,這表示它該重送這個封包,直到收到 ACK 為止。在實作可靠的 SOCK_DGRAM 應用程式時,這個回報的過程很重要。

對於無需可靠度的(unreliable)應用程式,如遊戲、音效、或影像,你只需忽略遺失的封包,或也許能試著用技巧彌補回來。(雷神之鎚的玩家都知道的一個技術名詞的影響:accursed lag。在這個例子中,"accursed"(受到詛咒)這個字代表各種低級的意思)。

為什麼你要用一個不可靠的底層協定?

有兩個理由:第一個理由是速度,第二個理由還是速度。直接忘了這個封包是比較快的方式,相較之下,持續追蹤全部的封包是否安全抵達,並確保依序抵達是比較慢的。如果你想要傳送聊天訊息,TCP 很讚;不過如果你想要替全世界的玩家,每秒送出 40 個位置更新的資訊,且若遺失一到兩個封包並不會有太大的影響時,此時 UDP 是一個好的選擇。

[8] http://tools.ietf.org/html/rfc768

譯註:
stream(串流式)socket 是指應用程式要傳輸的資料就如水流(串流)在水管中傳輸一般,經由這個 stream socket 流向目的,串流式 socket 是資料會由傳輸層負責處理遺失、依序送達等工作,以在傳輸層確保應用程式所送出的資料能夠可靠且依序抵達,而應用程式若對資料有可靠與依序的需求時,使用 stream socket 就不用自行處理這類的工作。

datagram(訊息式)socket 是基於訊息導向的方式傳送資料,應用程式送出的每筆資料會如平信的概念送出,由於遶送封包的路徑可能會隨著網路條件而改變,每筆資料抵達的順序不一定會按照送出的順序抵達,並且如平信般,信件可能在遞送過程遺失,而寄件人並無法知道是否遞送成功。

初步簡單知道應用這兩種 sockets 的時機:當需要資料能完整送達目地時,就使用 stream socket,若是部分資料遺失也無妨時,就可以使用 datagram socket。

2.2 底層漫談與網路理論

因為我只著重於協定的分層,該是談談網路是如何真正的運作的時候了,並呈現一些如何打造 SOCK_DGRAM 封包的例子。實務上,你或許可以忽略這一節,然而,這裡有很好的觀念背景。


嘿!孩子們,該是學習資料封裝(Data Encapsulation)的時候了。這很重要。它就是非常重要,所以就算你是在加州這裡上的網路課程,也只能學到皮毛。

基本上我們會講到這些內容:

封包的誕生、將封包打包["封裝"]到第一個協定[所謂的 TFTP 協定]的 header 中[幾乎是最底層了],接著將全部的東西[包含 TFTP header]封裝到下一個協定中[所謂的 UDP],接著下一個協定[IP],最後銜接到硬體[實體]層上面的通訊協定[所謂的 Ethernet,乙太網路]。

當另一台電腦收到封包時,硬體會解開 Ethernet header,而 kernel 會解開 IP 與 UDP header,再來由 TFTP 程式解開 TFTP header,最後程式可以取得資料。

現在我最後要談個聲名狼藉的分層網路模型(Layered Network Model),亦稱"ISO/OSI"。這個網路模型介紹了一個網路功能系統,有許多其它模型的優點。例如,你可以寫剛好一樣的 socket 程式,而不用管資料在實體上是怎麼傳送的[Serial、thin Ethernet、AUI 之類]。因為在底層的程式會幫你處理這件事。真正的網路硬體與拓樸對 socket 程式設計師而言是透明的。

不囉嗦,我將介紹這個成熟模型的分層。為了網路課程的測驗,要記住這些。

  • Application(應用層)
  • Presentation(表現層)
  • Session(會談層)
  • Transport(傳輸層)
  • Network(網路層)
  • Data Link(資料鏈結層)
  • Physical(實體層)

實體層就是硬體(serial、Ethernet 等)。而應用層你可以盡可能的想像,這是個使用者與網路互動的地方。

現在這個模型已經很普及,所以你如果願意的話,或許可以將它當作是一本汽車修理指南來使用。與 Unix 比較相容的分層模型有:

  • 應用層(Application layer:telnet、ftp 等)
  • 主機到主機的傳輸層(Transport layer:TCP、UDP)
  • 網際網路層(Internet layer:IP 與路由遶送)
  • 網路存取層(Network Access Layer:Ethernet、wi-fi、諸如此類)

此時,你或許能知道這幾層是如何對應到原始資料的封裝。

看看在打造一個簡單的封包需要多少工作呢?

天阿!你得自己用"cat"將資訊填入封包的 header 裡!

開玩笑的啦。

你對 stream socket 需要做的只有用 send() 將資料送出。而在 datagram socket 需要你做的是,用你所選擇的方式封裝該封包,並且用 sendto() 送出。Kernel 會自動幫你建立傳輸層與網路層,而硬體處理網路存取層。啊!真現代化的技術。

所以該結束我們短暫的網路理論之旅了。

喔!對了,我忘記告訴你我想要談談 routing(路由遶送)了。恩,沒事!沒關係,我不打算全部講完。

Router(路由器)會解開封包的 IP header,參考自己的 routing table(路由表、遶送表)…。如果你真的很想知道,你可以讀 IP RFC [9]。如果你永遠都不想碰它,其實你也可以過得很好。

[9] http://tools.ietf.org/html/rfc791

譯註:
若讀者想要深入了解網際網路或 TCP/IP 觀念,以下是譯者推薦的參考資料,這些都是網路 TCP/IP 觀念的經典著作。
[C1] Behrouz Forouzan, TCP/IP Protocol Suite, 4 edition, Mcgraw-Hill Inc., 2009.
[C2] Kevin R. Fall and W. Richard Stevens, TCP/IP Illustrated, The Protocols, Vol. 1, 2 edition, Addison-Wesley Inc., 2011.
[C3] Douglas E. Comer, Internetworking with TCP/IP principles, protocols, and architecture, Vol. 1, 6 edition, Pearson education Inc., 2013.
[C4] Pat Eyler, Networking Linux: A Practical Guide to TCP/IP, New Riders Inc., 2001.