6.2.2
Socket 的英文原意就是“孔”或“插座”,現在,作為 BSD UNIX 的進程通訊機制,
取其后一種意義。日常生活中常見的插座,有的是信號插座,有的是電源插座,有的可以
接受信號(或能量) ,有的可以發送信號(或能量)。假如電話線與電話機之間安放一個插
座(相當於二者之間的接口,這一部分裝置物理上是存在的)則 Socket 非常相似於電話插
座。
將電話系統與面向連接的 Socket 機制相比,有着驚人相似的地方。以一個國家級的電
話網為例。電話的通話雙方相當於相互通信的兩個進程;通話雙方所在的地區(享有一個
全局唯一的區號)相當於一個網絡,區號是它的網絡地址;區內的一個單位的交換機相當
於一台主機,主機分配給每個用戶的局內號碼相當於 Socket 號(下面將談到) 。

圖 6-1 socket 接口示意圖
任何用戶在通話之前,首先要占有一部電話機,相當於申請一個 Socket 號;同時要知
道對方的電話號碼,相當於對方有一個 Socket。然后向對方撥號呼叫,相當於發出連接請
求(假如對方不在同一區內,還要撥對方區號,相當於給出網絡地址) 。對方假如在場並
空閑(相當於通信的另一主機開機且可以接受連接請求) ,拿起電話話筒,雙方就可以正
式通話,相當於連接成功。雙方通話的過程,是向電話機發出信號和從電話機接受信號的
過程,相當於向 Socket 發送數據和從 Socket 接受數據。通話結束后,一方掛起電話機,
相當於關閉 Socket,撤消連接。
在電話系統中,一般用戶只能感受到本地電話機和對方電話號碼的存在,建立通話的
過程、話音傳輸的過程以及整個電話系統的技術細節對它都是透明的,這也與 Socket 機制
非常相似。Socket 利用網間網通信設施實現進程通信,但它對通信設施的細節毫不關心,
只要通信設施能提供足夠的通信能力,它就滿足了。
至此,我們對 Socket 進行了直觀的描述。抽象出來,Socket 實質上提供了進程通信的
端點。進程通信之前,雙方首先必須各自創建一個端點,否則是沒有辦法建立聯系並相互
通信的。正如打電話之前,雙方必須各自擁有一台電話機一樣。
每一個 Socket 都用一個半相關描述:
{協議,本地地址,本地端口}
一個完整的 Socket 則用一個相關描述
{協議,本地地址,本地端口,遠程地址,遠程端口}
每一個 Socket 有一個本地的唯一 Socket 號,由操作系統分配。
最重要的是,Socket 是面向客戶-服務器模型而設計的,針對客戶和服務器程序提供
不同的 Socket 系統調用。客戶隨機申請一個 Socket 號(相當於一個想打電話的人可以在
任何一台入網的電話上撥叫呼叫) ;服務器擁有全局公認的 Socket,任何客戶都可以向它
發出連接請求和信息請求(相當於一個被呼叫的電話擁有一個呼叫方知道的電話號碼) 。
Socket 利用客戶— 服務器模式巧妙的解決了進程之間建立通信連接的問題。服務器
Socket 為全局所公認非常重要。兩個完全隨機的用戶進程之間,因為沒有任何一方的 Socket
是固定的,就像打電話卻不知道別人的電話號碼,要通話是不可能的。
套接字的三種類型
6.2.3
套接字有三種類型:流式套接字 (SOCK_STREAM),數據報套接字 (SOCK_DGRAM)
及原始套接字。
1.流式套接字(SOCK_STREAM)
流式的套接字可以提供可靠的、面向連接的通訊流。如果你通過流式套接字發送了順
序的數據: “1”“2”
、 。那么數據到達遠程時候的順序也是“1”“2” 、 。
流式套接字可以做什么呢?你聽說過 Telnet 應用程序嗎?聽過?哦,最常用的 BBS 服
務,以及系統的遠程登陸都是通過 Telnet 協議連接的。Telnet 就是一個流式連接。你是否
希望你在 Telnet 應用程序上輸入的字符(或漢字)在到達遠程應用程序的時候是以你輸入
的順序到達的?答案應該是肯定的吧。還有 WWW 瀏覽器,它使用的 HTTP 協議也是通過
流式套接字來獲取網頁的。事實上,如果你 Telnet 到一個 Web Site 的 80 端口上,然后輸
入 “GET 網頁路徑名”然后按兩下回車(或者是兩下 Ctrl+回車)然后你就得到了“網頁
路徑名”所代表的網頁!
流式套接字是怎樣保證這種應用層次上的數據傳輸質量呢?它使用了 TCP ( The
Transmission Control Protocol)協議(可以參考 RFC-793 來得到 TCP 的細節) 。TCP 保證
了你的數據傳輸是正確的,並且是順序的。TCP 是經常出現的 TCP/IP 中的前半部分。IP
代表 Internet Protocol(因特網協議,參考 RFC-791)IP 只處理網絡路由。
圖 6-2 面向連接的 socket 的工作流程

2.數據報套接字(SOCK_DGRAM)
數據報套接字定義了一種無連接的服務,數據通過相互獨立的報文進行傳輸,是無序
的,並且不保證可靠,無差錯。原始套接字允許對低層協議如 IP 或 ICMP 直接訪問,主要
用於新的網絡協議實現的測試等。
數據報套接字(Datagram Sockets)怎樣呢?為什么它叫做“無連接”?應該怎樣處理
它們呢?為什么它們是不可靠的?好的,這里有一些事實:
l 如果你發送了一個數據報,它可能不會到達。
l 它可能會以不同的順序到達。
l 如果它到達了,它包含的數據中可能存在錯誤。
數據報套接字也使用 IP,但是它不使用 TCP,它使用使用者數據報協議 UDP(User
Datagram Protocol 可以參考 RFC 768)
為什么說它們是“無連接”的呢?因為它(UDP)不像流式套接字那樣維護一個打開
的連接,你只需要把數據打成一個包,把遠程的 IP 貼上去,然后把這個包發送出去。這個
過程是不需要建立連接的。UDP 的應用例子有: tftp, bootp 等。
那么,數據包既然會丟失,怎樣能保證程序能夠正常工作呢?事實上,每個使用 UDP
的程序都要有自己的對數據進行確認的協議。比如, TFTP 協議定義了對於每一個發送出
去的數據包,遠程在接受到之后都要回送一個數據包告訴本地程序: “我已經拿到了!(一
”
個 “ACK” 包) 。如果數據包發的送者在 5 秒內沒有的得到回應,它就會重新發送這個
數據包直到數據包接受者回送了 “ACK” 信號。這些知識對編寫一個使用 UDP 協議的
程序員來說是非常必要的。
無連接服務器一般都是面向事務處理的,一個請求一個應答就完成了客戶程序與服務
程序之間的相互作用。若使用無連接的套接字編程,程序的流程可以用圖 6-3 表示。

圖 6-3 無連接的 socket 工作流程
面向連接服務器處理的請求往往比較復雜,不是一來一去的請求應答所能解決的,而
且往往是並發服務器。使用面向連接的套接字編程,可以通過圖 6-2 來表示。
套接字工作過程如下:服務器首先啟動,通過調用 socket()建立一個套接字,然后調用
bind()將該套接字和本地網絡地址聯系在一起,再調用 listen()使套接字做好偵聽的准備,
並規定它的請求隊列的長度,之后就調用 accept()來接收連接。客戶在建立套接字后就可調
用 connect()和服務器建立連接。連接一旦建立,客戶機和服務器之間就可以通過調用 read()
和 write()來發送和接收數據。最后,待數據傳送結束后,雙方調用 close()關閉套接字。
3.原始套接字
原始套接字主要用於一些協議的開發,可以進行比較底層的操作。它功能強大,但是
沒有上面介紹的兩種套接字使用方便,一般的程序也涉及不到原始套接字。