1. 程式人生 > >C語言之socket概述

C語言之socket概述

在Linux中,一切都是檔案,除了文字檔案、原始檔、二進位制檔案等,一個硬體裝置也可以被對映為一個虛擬的檔案,稱為裝置檔案。例如,stdin 稱為標準輸入檔案,它對應的硬體裝置一般是鍵盤,stdout 稱為標準輸出檔案,它對應的硬體裝置一般是顯示器。對於所有的檔案,都可以使用 read() 函式讀取資料,使用 write() 函式寫入資料。

“一切都是檔案”的思想極大地簡化了程式設計師的理解和操作,使得對硬體裝置的處理就像普通檔案一樣。所有在Linux中建立的檔案都有一個 int 型別的編號,稱為檔案描述符(File Descriptor)。使用檔案時,只要知道檔案描述符就可以。例如,stdin 的描述符為 0,stdout 的描述符為 1。

在Linux中,socket 也被認為是檔案的一種,和普通檔案的操作沒有區別,所以在網路資料傳輸過程中自然可以使用與檔案 I/O 相關的函式。可以認為,兩臺計算機之間的通訊,實際上是兩個 socket 檔案的相互讀寫。

檔案描述符有時也被稱為檔案控制代碼(File Handle),但“控制代碼”主要是 Windows 中術語,所以本教程中如果涉及到 Windows 平臺將使用“控制代碼”,如果涉及到 Linux 平臺將使用“描述符”。

在Linux下建立 socket

在 Linux 下使用 <sys/socket.h> 標頭檔案中 socket() 函式來建立套接字,原型為:
int socket(int af, int type, int protocol);
1) af 為地址族(Address Family),也就是 IP 地址型別,常用的有 AF_INET 和 AF_INET6。AF 是“Address Family”的簡寫,INET是“Inetnet”的簡寫。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。

大家需要記住127.0.0.1,它是一個特殊IP地址,表示本機地址,後面的教程會經常用到。
你也可以使用PF字首,PF是“Protocol Family”的簡寫,它和AF是一樣的。例如,PF_INET 等價於 AF_INET,PF_INET6 等價於 AF_INET6。
2) type 為資料傳輸方式,常用的有 SOCK_STREAM 和 SOCK_DGRAM,在《socket是什麼意思》一節中已經進行了介紹。

3) protocol 表示傳輸協議,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分別表示 TCP 傳輸協議和 UDP 傳輸協議。

有了地址型別和資料傳輸方式,還不足以決定採用哪種協議嗎?為什麼還需要第三個引數呢?

正如大家所想,一般情況下有了 af 和 type 兩個引數就可以建立套接字了,作業系統會自動推演出協議型別,除非遇到這樣的情況:有兩種不同的協議支援同一種地址型別和資料傳輸型別。如果我們不指明使用哪種協議,作業系統是沒辦法自動推演的。

該教程使用 IPv4 地址,引數 af 的值為 PF_INET。如果使用 SOCK_STREAM 傳輸資料,那麼滿足這兩個條件的協議只有 TCP,因此可以這樣來呼叫 socket() 函式:
int tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  //IPPROTO_TCP表示TCP協議
這種套接字稱為 TCP 套接字。

如果使用 SOCK_DGRAM 傳輸方式,那麼滿足這兩個條件的協議只有 UDP,因此可以這樣來呼叫 socket() 函式:
int udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);  //IPPROTO_UDP表示UDP協議
這種套接字稱為 UDP 套接字。

上面兩種情況都只有一種協議滿足條件,可以將 protocol 的值設為 0,系統會自動推演出應該使用什麼協議,如下所示:
int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);  //建立TCP套接字
int udp_socket = socket(AF_INET, SOCK_DGRAM, 0);  //建立UDP套接字
後面的教程中多采用這種簡化寫法。

在Windows下建立socket

Windows 下也使用 socket() 函式來建立套接字,原型為:
SOCKET socket(int af, int type, int protocol);
除了返回值型別不同,其他都是相同的。Windows 不把套接字作為普通檔案對待,而是返回 SOCKET 型別的控制代碼。請看下面的例子:
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);  //建立TCP套接字