1. 程式人生 > >《unix網路程式設計》(8)基本TCP套接字

《unix網路程式設計》(8)基本TCP套接字

套接字函式


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);稱其返回值為已連線套接字描述符

區分兩個套接字:

         伺服器通常只有一個監聽套接字,在該伺服器生命週期內一直存在。核心為每個伺服器接受的客戶連線建立一個已連線套接字(三次握手完成);當伺服器完成對客戶的服務時,相應的已連線套接字被關閉