天天看點

《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);稱其傳回值為 已連接配接套接字描述符。

區分兩個套接字:

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

繼續閱讀