上一篇文章我們簡單講了TCP服務端和客戶端程序的編寫,但是對於裏面函數並沒有具體的說明。接下來具體說明一下 socket 函數。
socket 函數主要用於創建套接字,它的基本形式如下:
- int socket (int domain, int type, int protocel)
- domain: 套接字中使用的協議族信息
- type: 套接字傳輸類型信息
- protocol: 計算機間通信中使用的協議信息
- 返回值: 成功時返回文件描述符,失敗時返回-1
1. 協議族
socket 函數的第一個參數爲套接字中使用的協議分類信息,成爲協議族,可分爲如下幾類:
- PF_INET : IPv4互聯網協議族
- PF_INET6: IPv6互聯網協議族
- PF_LOCAL : 本地通信的UNIX協議族
- PF_PACKET : 底層套接字的協議族
- PF_IPX : IPX Novell協議族
之前的例子中,使用的是 PF_INET 協議族,另外套接字中實際採用的最終協議信息是通過 socket 函數的第三個參數傳遞的。在指定的協議族範圍內通過第一個參數決定第三個參數。
2. 套接字類型
套接字類型指的是套接字數據的傳輸方式,即使用TCP還是UDP傳輸。
(1)面向連接的套接字(SOCK_STREAM)
如果向 socket 函數的第二個函數傳遞 SOCK_STREAM , 將創建面向連接套接字。面向連接套接字具有如下特點
- 傳輸過程中數據不會丟失。
- 按順序傳輸數據。
- 傳輸的數據不存在數據邊界。
- 套接字必須一一對應。
收發數據的套接字中具有緩衝區,通過套接字傳輸的數據保存在緩衝區中,因此收到數據並不意味之馬上調用 recv 函數,只要不超過緩衝區的數據容量,則有可能在數據填充滿緩衝後調用一次read函數讀取全部數據。也可能分多次read函數進行讀取。因此調用 read 和 write 函數的次數沒有太大的意義。所以說 傳輸的數據是不存在數據邊界的。
如果緩衝區的數據滿了會怎麼樣呢? 首先調用 read 函數讀取緩衝區的部分數據,如果 read 函數的讀取速度比接受的速度滿,則緩衝取可能被填滿。此時套接字無法再接受數據,但即使這樣也不會發生數據丟失,因此傳輸數據的套接字將停止傳輸。也就是說,面向連接的套接字會根據接收端的狀態傳輸數據,如果傳輸出錯還會重傳。
用一句話總結面向連接的套接字就是:
可靠的、按序傳輸的、基於字節的面向連接的數據傳輸方式的套接字
(2)面向消息的套接字(SOCK_DGRAM)
如果向 socket 函數的第二個參數傳遞 SOCK_DGRAM 參數,則創建面向消息的套接字。面向消息的套接字有如下特點:
- 強調快速傳輸而非傳輸順序。
- 傳輸的數據可能丟失也可能被銷燬。
- 傳輸的數據有數據邊界。
- 限制每次傳輸數據的大小。
面向消息的套接字比面向連接的套接字具有更快的傳輸速度,但是無法避免的數據丟失和銷燬。另外,每次傳輸數據大小具有一定的限制,並存在數據邊界。存在數據邊界意味着接收數據的次數和傳輸次數相同,總結面向消息的套接字一句話就是:
不可靠的、不按序傳遞的、以數據的高速傳輸爲目的的套接字。
3. 協議的最終選擇
下面介紹 socket 函數的最後一個參數,該參數決定最終採用的協議。前面的程序中,最後一個參數填的0,除非遇到如下情況:
同一協議族中存在多個數據傳輸方式相同的協議
在參數 PF_INET 指 IPv4 網絡族中,SOCK_STREAM 面向連接的數據傳輸。滿足以上兩個條件的只有 IPPROTO_TCP,因此可以使用下面的方式創建 TCP套接字
int tcpsocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
SOCK_DGRAM 指向的面向消息的傳輸方式中,滿足以上條件的只有 IPPROTO_UDP, 因此可以使用下面的方式創建 UDP套接字
int udpsocket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
4. Windows中的socket函數
上面介紹的爲 Linux 的socket函數,windows中socket函數跟Linux中的使用是相同,唯一不同的是返回值,這裏再次給出 socket 函數的聲明:
- #include <winsock2.h>
- SOCKET socket(int domain, int type, int protocel)
- 返回值:成功返回socket句柄,失敗返回 INVALID_SOCKET