Linux學習之網路程式設計(TCP程式設計 模型總結)
言之者無罪,聞之者足以戒。 - “詩序”
TCP通訊也就是伺服器和客戶端的一種通訊方式,它的整體框架為:
針對TCP通訊所用到的函式,我來做一下說明:
(1)插座創造一個套接字
int socket(int domain,int type,int protocol)
標頭檔案的:#include <SYS / socket.h>中
第一個引數:通訊域,確定通訊特性,包括地址格式域描述
第二個引數:套接字型別
第三個引數:指定相應的傳輸協議
返回值:成功則返回套接字檔案描述符,失敗返回-1
引數域:通訊域,確定通訊特性,包括地址格式域描述
引數協議:指定相應的傳輸協議,也就是諸如TCP或UDP協議等等,系統針對每一個協議簇與型別提供了一個預設的協議,我們通過把協議設定為0來使用這個預設的值。
family: | type: | 組合 | AF_INET | AF_INET6 | AF_LOCAL | AF_PACKET | |||
簇 | 解釋 | SOCK_STREAM | 位元組 流套接字 | TCP | TCP | YES | |||
AF_INET | IPv4協議 | SOCK_DGRAM | 資料包套接字 | UDP | UDP | YES | |||
AF_INET6 | IPv6協議 | SOCK_RAM | 原始套接字 | IPv4 | IPv6 | ethernet | |||
AF_LOCAL/AF_UNIX | 本地套接字 | ||||||||
AF_PACKET |
(2)建立與TCP伺服器的連線
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen)
第一個引數:是由socket函式返回的套接字描述符
第二個引數:指向套接字地址結構的指標
第三個引數:地址結構的大小
套接字地址結構必須包含有伺服器的IP地址和埠號
返回值:成功返回0,出錯返回-1
客戶在呼叫connect之前不必非得呼叫bind函式,因為如果需要的話,核心會確定源IP地址並選擇一個臨時埠作為埠號
如果是TCP套接字,呼叫connect函式將激發TCP的三次握手過程,而且僅在連線建立成功或出錯時才返回,其中,出錯返回的可能情況有以下幾種:
1)若TCP客戶沒有收到SYN分節的響應,則返回ETIMEDOUT錯誤。舉例,呼叫connect函式時,4.4BSD核心傳送一個SYN,若無響應則等待6s後再發送一個,若仍無響應則等待24s後再發送一個。若總共等待了75s後仍未收到響應則返回本錯誤
2)若對客戶的SYN的響應是RST(表示復位),則表明該伺服器主機在我們指定的埠上沒有程序在等待與之連線。這是一種硬錯誤,客戶一收到RST就馬上返回ECONNREFUSED錯誤。
3)若客戶發出的SYN在中間的某個路由器上引發了一個“destination unreachable”(目的地不可達)ICMP錯誤,則認為是一個軟錯誤。客戶主機核心儲存該訊息,並按第一種情況所述的時間間隔繼續傳送SYN。若在某個規定的時間內仍無響應,則把儲存的資訊(即ICMP錯誤)作為EHOSTUNREACH或ENETUNREACH錯誤返回給程序。以下兩種情況是有可能的:一是按照本地系統的轉發表,根本沒有到達遠端系統的路徑;二是connect呼叫根本不等待就返回。
(3)結合繫結伺服器的地址和埠到插座
這樣做就是讓客戶端來發現用以連線的伺服器的地址
int bind(int sockfd,const struct sockaddr * addr,socklen_t len)
標頭檔案:#include <sys / socket.h>
第一個引數:是由socket函式返回的套接字描述符
第二個引數:伺服器的地址
第三個引數:地址的長度
返回值:成功返回0,失敗返回-1
引數地址:伺服器的地址,對於因特網域,如果設定地址為INADDR_ANY,套接字可以繫結到所有的網路埠。這意味著可以收到這個系統所有網絡卡的資料包。一般我們,在使用SOCKADDR_IN型別的結構體代替的sockaddr行業釋義體系結構
引數len:指定地址的長度
下面羅列一下常見的幾種出錯情況:
1)IP地址可以選擇通配地址,埠號為0表示(核心分配臨時埠)
2)INADDR_ANY,由核心選擇IP介面,這個巨集的值為0(注意:該巨集對於IPv4是可行的)
3)分配臨時埠號,需要用函式getsockname,來返回 你的協議地址
4)繫結通常會返回的錯誤:Address already in use ,通過套接字選項來設定,setsockopt,SO_REUSEADDR
(4)設定允許的最大連線數
int listen(int sockfd,int backlog)
標頭檔案:#include <sys / socket.h>
第一個引數:是由socket函式返回的套接字描述符
第二個引數:用於表示伺服器能接受的請求數量
返回值:成功返回0,失敗返回-1
伺服器呼叫監聽函式來宣告可以接受連線請求
關於listen函式的相關內容可以參考https://www.cnblogs.com/lengender-12/p/6813057.html
(5)接受等待來自客戶端的連線請求
int accept(int sockfd,struct sockaddr * restrict addr,socklen_t * restrict len)
標頭檔案的:#include <SYS / socket.h>中
第一個引數:是由socket函式返回的套接字描述符
第二個引數:用來存放客戶端的地址,如果地址的空間足夠大,系統會自動填充。
第三個引數:地址的長度
返回值:成功則返回套接字描述符,失敗返回-1
一旦伺服器呼叫了聽,套接字就能接收連線請求。使用接受函式來接受並建立請求
注意:
1,接受返回一個新的socket關聯到客戶端,它與原始的socket有相同的套接字型別和協議族。傳遞給接受的原始socket並沒有關聯客戶端,它要繼續保持可用狀態,接收其他請求。
2,接受是一個阻塞的函式,會一直等到有客戶端的請求。