套接字函數
socket函數
建立一個套接字用于通信:
#include <sys/socket.h>
int socket(int family, int type, int protocol); //成功傳回非負的描述符,出錯傳回-1
/*
參數:
family:指定通信協定族(protocol family),常用取值AF_INET(IPv4)
type:指定socket類型, 流式套接字SOCK_STREAM,資料報套接字SOCK_DGRAM,原始套接字SOCK_RAW
protocol:協定類型,常用取值0, 使用預設協定
*/
bind函數
把一個本地協定位址賦予一個套接字,對網際協定,協定位址是32位IPv4位址或128位IPv6位址與16位TCP或UDP端口的組合。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
bind第二個參數是 const的,是以無法傳回所選的值。是以,如果核心配置設定臨時端口(指定端口為0則核心在bind調用時選擇一個臨時端口),那麼 為了擷取臨時端口的值隻能使用getsockname來傳回協定位址。如果指定通配IP,那麼核心将在 套接字已連接配接(TCP)或 已在套接字上發出資料報時(UDP)才選擇一個本地IP。
bind可以指定一個端口号或一個IP也可以兩個都不指定:
(1)TCP客戶或伺服器未曾綁定一個端口,當調用connect或listen時,核心為響應的套接字選擇臨時端口。這對于TCP客戶是正常的,但對于TCP伺服器卻很罕見,因為TCP伺服器是通過衆所周知的端口來連接配接的。
(2)當綁定IP:對用戶端來說就指定了源IP位址;對伺服器來說就它就隻接收從這個IP位址發起的連接配接。是以通常伺服器不綁定IP。當客戶連接配接套接字時,核心根據所用外出網絡接口選擇源IP。伺服器沒綁定IP的話,核心會把客戶發來的SYN的目的位址作為伺服器的源IP。
connect函數
TCP客戶與TCP伺服器建立連接配接。
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
參數:
sockfd:未連接配接套接字
addr:要連接配接的套接字位址
addrlen:第二個參數addr長度
*/
客戶調用connect前不必非要調用bind,因為如果需要,核心會确定源IP并選擇臨時端口。
如果是TCP套接字,調用connect會激發三次握手,而且僅連接配接建立成功或出錯才傳回,其中錯誤有以下幾種:
(1)客戶沒收到SYN分節的響應,傳回ETIMEDOUT錯誤。會發幾次SYN,仍未收到響應會傳回該錯誤。
(2)若對客戶SYN響應是RST(複位),表明在我們連接配接的端口上沒有程序在等待連接配接(例如伺服器沒運作)。這是錯誤,客戶收到RST立即傳回ECONNREFUSED錯誤。
(3)若客戶發出的SYN在中間某路由上引發“destination unreachable”(目的地不可達)ICMP錯誤,這是軟錯誤。客戶核心儲存該資訊,繼續隔一段時間就發SYN,若仍沒響應,則儲存ICMP錯誤作為EHOSTUNREACH或ENETUNREACH錯誤傳回給程序。
按照TCP狀态轉換圖(參考《unix網絡程式設計》(3)TCP連接配接的建立和終止),connect導緻目前套接字由CLOSED狀态轉移到SYN_SENT狀态,若成功則轉移到ESTABLISHED狀态。
若connect失敗則該套接字不可用,必須關閉,不能對它再次調用connect,必須close描述符然後重新調用socket。
listen函數
該函數僅由TCP伺服器調用,完成兩件事:
(1)listen函數應該用在調用socket和bind函數之後, 并且用在調用accept之前。用于将一個套接字從一個主動套接字(socket建立的是主動套接字)轉變成為被動套接字,訓示核心應該接受指向該套接字的連接配接請求。
(2)第二個參數指定核心應該為相應套接字排隊的最大連接配接個數。
int listen(int sockfd, int backlog);
/*
backlog說明:對于給定的監聽套接口,核心要維護兩個隊列: 1、已由客戶發出并到達伺服器,伺服器正在等待完成相應的TCP三路握手過程(SYN_RCVD狀态)
2、已完成連接配接的隊列(ESTABLISHED狀态)但是兩個隊列長度之和不能超過backlog
*/
按照TCP狀态轉換圖(參考 《unix網絡程式設計》(3)TCP連接配接的建立和終止 ),調用listen導緻套接字從CLOSED轉換到LISTEN。
accept函數
該函數由TCP伺服器調用。用于從已經完成連接配接隊列對頭傳回下一個已完成連接配接;若已完成隊列空,則程序睡眠(如果套接字為預設的阻塞方式)。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/*
參數:
sockfd:伺服器套接字
addr:将傳回對等方的套接字位址, 不關心客戶身份的話, 可以設定為NULL
addrlen:傳回對等方的套接字位址長度, 不關心的話可以設定成為NULL, 否則一定要初始化
*/
注意:accept函數稱其第一個參數為 監聽套接字描述符(由socket建立,用于bind和listen);稱其傳回值為 已連接配接套接字描述符。
區分兩個套接字:
伺服器通常隻有一個監聽套接字,在該伺服器生命周期内一直存在。核心為每個伺服器接受的客戶連接配接建立一個已連接配接套接字(三次握手完成);當伺服器完成對客戶的服務時,相應的已連接配接套接字被關閉。