TCP/IP 編程接口提供各種系統(tǒng)調(diào)用,以幫助您有效地使用該協(xié)議。TCP 堆棧代碼數(shù)量繁多,深入到內(nèi)核級別的完整調(diào)用序列可以幫助您了解 TCP 堆棧。在本文中,將回顧和學(xué)習(xí)關(guān)于 TCP 調(diào)用序列的詳細(xì)信息,其中包括對 FreeBSD 的引用,以及在用戶級進(jìn)行系統(tǒng)調(diào)用后在 TCP 堆棧中發(fā)生的重要函數(shù)調(diào)用。 引言 典型的 TCP 客戶機(jī)和服務(wù)器應(yīng)用程序通過發(fā)布 TCP 系統(tǒng)調(diào)用序列來獲取某些函數(shù)。這些系統(tǒng)調(diào)用包括 socket () 、bind () 、listen () 、accept () 、send () 和 receive() 。本文介紹在應(yīng)用程序發(fā)布 TCP 系統(tǒng)調(diào)用時在較低級別中發(fā)生的情況,如圖 1 所示。 圖 1. TCP 應(yīng)用程序進(jìn)行的普通調(diào)用序列
圖 2 顯示了 TCP 系統(tǒng)調(diào)用在物理鏈路上發(fā)出之前進(jìn)行傳播的各個層。 圖 2. TCP 系統(tǒng)調(diào)用的各個層
套接字層接收進(jìn)行的任何 TCP 系統(tǒng)調(diào)用。套接字層驗(yàn)證 TCP 應(yīng)用程序傳遞的參數(shù)的正確性。這是一個獨(dú)立于協(xié)議 的層,因?yàn)樯形磳f(xié)議連接到調(diào)用中。 套接字層下面是協(xié)議層,該層包含協(xié)議的實(shí)際實(shí)現(xiàn)(本例中為 TCP)。當(dāng)套接字層對協(xié)議層進(jìn)行調(diào)用時,將確保對兩個層之間共享的數(shù)據(jù)結(jié)構(gòu)具有獨(dú)占訪問權(quán)限。這樣做是為了避免任何數(shù)據(jù)結(jié)構(gòu)損壞。 各種網(wǎng)絡(luò)設(shè)備驅(qū)動程序在接口層運(yùn)行,該層從物理鏈路接收數(shù)據(jù),并向物理鏈路傳輸數(shù)據(jù)。 每個套接字具有一個套接字隊列,并且每個接口具有一個用于數(shù)據(jù)通信的接口隊列。不過,對于整個協(xié)議層,只有一個稱為 IP 輸入隊列的協(xié)議隊列。接口層通過此 IP 輸入隊列將數(shù)據(jù)輸入到協(xié)議層。協(xié)議層使用相應(yīng)的接口隊列將數(shù)據(jù)輸出到接口。 在本文中,將學(xué)習(xí)以下系統(tǒng)調(diào)用: Socket socket (struct proc *p, struct socket_args *uap, int retval)
struct sock_args
{
int domain,
int type,
int protocol;
};
|
在 socket 系統(tǒng)調(diào)用中: p 是一個指針,指向進(jìn)行 socket 調(diào)用的進(jìn)程的 proc 結(jié)構(gòu)。uap 是一個指向 socket_args 結(jié)構(gòu)的指針,該結(jié)構(gòu)包含傳遞到 socket 系統(tǒng)調(diào)用中的進(jìn)程的參數(shù)。retval 是系統(tǒng)調(diào)用的返回值。
socket 系統(tǒng)調(diào)用通過分配新的描述符創(chuàng)建新的套接字。將新的描述符返回到調(diào)用進(jìn)程。任何后續(xù)的系統(tǒng)調(diào)用都使用創(chuàng)建的套接字標(biāo)識。socket 系統(tǒng)調(diào)用還向創(chuàng)建的套接字描述符分配協(xié)議。
domain 、type 和 protocol 參數(shù)值指定系列、類型和協(xié)議,以分配給創(chuàng)建的套接字。圖 3 顯示了調(diào)用序列。
圖 3. 用于 socket 系統(tǒng)調(diào)用的調(diào)用序列
從進(jìn)程檢索參數(shù)后,socket 函數(shù)調(diào)用 socreate 函數(shù)。socreate 函數(shù)根據(jù)進(jìn)程指定的參數(shù)發(fā)現(xiàn)指向協(xié)議切換 protsw 結(jié)構(gòu)的指針。socreate 函數(shù)然后分配新的套接字結(jié)構(gòu)。然后進(jìn)行協(xié)議特定的調(diào)用 pr_usrreq ,進(jìn)而切換到與套接字描述符關(guān)聯(lián)的相應(yīng)協(xié)議特定的請求。pr_usrreq 函數(shù)的原型為: int pr_usrreq(struct socket *so , int req, struct mbuf *m0 , *m1 , *m2);
|
在 pr_usrreq 函數(shù)中:so 是指向套接字結(jié)構(gòu)的指針。req 的功能是標(biāo)識請求。本例中為 PRU_ATTACH。m0 、m1 和 m2 是指向 mbuf 結(jié)構(gòu)的指針。值因請求而異。
pr_usrreq 函數(shù)為大約 16 個請求提供服務(wù)。
tcp_usrreq() 函數(shù)調(diào)用 tcp_attach( ) ,以處理 PRU_ATTACH 請求。要分配 Internet 協(xié)議控制塊,可調(diào)用in_pcballoc() 。在 in_pcballoc 中,調(diào)用了內(nèi)核的內(nèi)存分配器函數(shù),該函數(shù)將內(nèi)存分配給 Internet 控制塊。完成所有必要的 Internet 控制塊結(jié)構(gòu)指針初始化之后,該控制返回到 tcp_attach() 。
分配新的 TCP 控制塊,并在 tcp_newtcpcb() 中初始化。它還初始化所有的 TCP 定時器變量,并且控制返回到 tcp_attach() ?,F(xiàn)在套接字狀態(tài)初始化為 CLOSED。在返回到 tcp_usrreq 函數(shù)時,創(chuàng)建套接字描述符,以指向套接字的 TCP 控制塊。 Internet 控制塊是雙向鏈接的循環(huán)鏈表,其指針指向套接字結(jié)構(gòu),同時套接字結(jié)構(gòu)的 so_pcb 部分指向 Internet 控制塊結(jié)構(gòu)。Internet 控制塊還具有指向 TCP 控制塊的指針。有關(guān) Internet 控制塊和 TCP 控制塊結(jié)構(gòu)的更詳細(xì)信息,請參見參考資料部分。 Bind bind (struct proc *p, struct bind_args *uap, int *retval)
struct bind_args
{ int s;
caddr_t name;
int namelen;
};
|
在 bind 系統(tǒng)調(diào)用函數(shù)中: s 是套接字描述符。name 是指向包含網(wǎng)絡(luò)傳輸?shù)刂返木彌_區(qū)的指針。namelen 是緩沖區(qū)的大小。
bind 系統(tǒng)調(diào)用將本地網(wǎng)絡(luò)傳輸?shù)刂放c套接字關(guān)聯(lián)。對于客戶端進(jìn)程,發(fā)布 bind 調(diào)用不是強(qiáng)制的。當(dāng)客戶端進(jìn)程發(fā)布 connect 系統(tǒng)調(diào)用時,內(nèi)核負(fù)責(zé)執(zhí)行隱式綁定。服務(wù)器進(jìn)程接受連接或啟動與客戶端的通信之前,發(fā)布顯式綁定請求通常是必需的。
bind 調(diào)用將進(jìn)程指定的本地地址復(fù)制到 mbuf ,并調(diào)用 sobind ,后者則根據(jù)請求使用 PRU_BIND 調(diào)用tcp_usrreq() 。tcp_usrreq() 中的切換實(shí)例調(diào)用 in_pcbbind() ,后者將本地地址和端口號綁定到套接字。in_pcbbind 函數(shù)首先執(zhí)行一些完整性檢查,以確保不綁定套接字兩次,并且至少一個接口分配了 IP 地址。in_pcbbind 負(fù)責(zé)隱式和顯式綁定。
如果對 in_pcbbind() (指向 sockaddr_in 結(jié)構(gòu)的指針)的調(diào)用中的第二個參數(shù)為非空,則發(fā)生顯式綁定。其他情況下,則發(fā)生隱式綁定。對于顯式綁定,在綁定的 IP 地址上執(zhí)行檢查,并相應(yīng)設(shè)置套接字選項。 圖 4. 用于 bind 系統(tǒng)調(diào)用的調(diào)用序列
如果指定的本地端口是一個非零值,則對超級用戶特權(quán)進(jìn)行檢查,以確定綁定是否位于保留的端口(例如,根據(jù) Berkley 約定,端口號 < 1024)。然后調(diào)用 in_pcblookup() ,以便查找具有提到的本地 IP 地址和本地端口號的控制塊。in_pcblookup() 驗(yàn)證本地地址和端口對是否仍未使用。如果 in_pcbbind() 中的第二個參數(shù)是 NULL,或本地端口是零,則控制失敗,并檢查臨時端口(例如,根據(jù) Berkley 約定,1024 < 端口號 < 5000)。然后調(diào)用 in_pcblookup() ,以驗(yàn)證發(fā)現(xiàn)的端口是否未使用。 Listen listen (struct proc *p, struct listen_args *uap, int *retval)
struct listen_args
{ int s;
int backlog;
};
|
在 listen 系統(tǒng)調(diào)用中: s 是套接字描述符。backlog 是套接字上的連接數(shù)的隊列限制。
listen 調(diào)用指示協(xié)議,服務(wù)器進(jìn)程準(zhǔn)備接受套接字上任何新傳入的連接。存在一個可以排列的連接數(shù)限制,在該連接數(shù)之后,忽略任何進(jìn)一步的連接請求。
listen 系統(tǒng)調(diào)用使用套接字描述符和 listen 調(diào)用中指定的backlog 值調(diào)用 solisten 。solisten 僅使用 PRU_LISTEN 作為請求調(diào)用 tcp_usrreq 函數(shù)。在 tcp_usrreq() 函數(shù)的切換語句中,PRU_LISTEN 的實(shí)例檢查套接字是否綁定到端口。如果端口為零,則調(diào)用 in_pcbbind() ,將套接字綁定到一個端口(按照 Bind 部分中的描述)。
如果端口上已存在偵聽的套接字,則將套接字的狀態(tài)更改為 LISTEN。通常,所有的服務(wù)器進(jìn)程都偵聽眾所周知的端口號。很少調(diào)用in_pcbbind 來執(zhí)行服務(wù)器進(jìn)程的隱式綁定。圖 5 顯示了偵聽的調(diào)用序列。 圖 5. 用于 listen 系統(tǒng)調(diào)用的調(diào)用序列
Accept accept(struct proc *p, struct accept_args *uap, int *retval);
struct accept_args
{
int s;
caddr_t name;
int *anamelen;
};
|
在 accept 系統(tǒng)調(diào)用中: s 是套接字描述符。name 是緩沖區(qū)(OUT 參數(shù)),它包含外來主機(jī)的網(wǎng)絡(luò)傳輸?shù)刂贰?/li>anamelen 是 name 緩沖區(qū)的大小。
accept 系統(tǒng)調(diào)用是等待傳入連接的阻塞調(diào)用。處理連接請求后,accept 將返回新的套接字描述符。將此新的套接字連接到客戶端,使另外一個套接字 s 保持 LISTEN 狀態(tài),以接受進(jìn)一步連接。
圖 6. 用于 accept 系統(tǒng)調(diào)用的調(diào)用序列
accept 調(diào)用首先驗(yàn)證參數(shù),并等待要到達(dá)的連接請求。在此之前,函數(shù)在 while 循環(huán)中阻塞。新的連接到達(dá)后,協(xié)議層喚醒服務(wù)器進(jìn)程。Accept 然后檢查函數(shù)阻塞時發(fā)生的任何套接字錯誤。如果存在任何套接字錯誤,則函數(shù)返回,并繼續(xù)從隊列拾取新的連接并調(diào)用soaccept 。在 soaccept() 中調(diào)用 tcp_usrreq () 函數(shù),并將請求作為 PRU_ACCEPT。tcp_usrreq 函數(shù)中的切換調(diào)用in_setpeeraddr() ,后者從協(xié)議控制塊復(fù)制外來 IP 地址和外來端口號,并將其返回到服務(wù)器進(jìn)程。
Connect connect (struct proc *p, struct connect_args *uap, int *retval);
struct connect_args
{
int s;
caddr_t name;
int namelen;
};
|
在 connect 系統(tǒng)調(diào)用中: s 是套接字描述符。name 是指向具有外來 IP/端口地址對的緩沖區(qū)的指針。namelen 是緩沖區(qū)的長度。
客戶端進(jìn)程通常調(diào)用 connect 系統(tǒng)調(diào)用,以連接到服務(wù)器進(jìn)程。如果在初始化連接之前,客戶端進(jìn)程沒有顯式發(fā)布 bind 系統(tǒng)調(diào)用,則堆棧負(fù)責(zé)本地套接字上的隱式綁定。 connect 系統(tǒng)調(diào)用將外來地址(需要將連接請求發(fā)送到地址)從進(jìn)程復(fù)制到內(nèi)核,并調(diào)用 soconnect() 。從 soconnect() 返回時,connect() 函數(shù)進(jìn)入睡眠狀體,直到協(xié)議層將其喚醒,并指示連接是 ESTABLISHED 或套接字上存在錯誤。soconnect() 函數(shù)檢查套接字的有效狀態(tài),并使用 PRU_CONNECT 作為請求調(diào)用 pr_usrreq() 。
tcp_usrreq() 函數(shù)中的切換實(shí)例檢查套接字與本地端口的綁定。如果未綁定套接字,則調(diào)用執(zhí)行隱式綁定的 in_pcbbind() 。然后調(diào)用 in_pcbconnect() ,以獲取到達(dá)目的地的路線,發(fā)現(xiàn)必須輸出套接字的接口,并驗(yàn)證 connect() 指定的外來套接字對(IP 地址和端口號)是否唯一。然后使用外來 IP 地址和端口號更新其 Internet 控制塊,并返回到 PRU_CONNECT 示例語句。
tcp_usrreq () 現(xiàn)在調(diào)用 soisconnecting () ,它可以將客戶端主機(jī)上的套接字的狀態(tài)設(shè)置為 SYN_SENT。調(diào)用函數(shù)tcp_output ,將 SYN 包輸出到網(wǎng)絡(luò)??刂片F(xiàn)在返回到 connect() 函數(shù),該函數(shù)處于睡眠狀態(tài),直到協(xié)議層喚醒 — 指示連接現(xiàn)在是 ESTABLISHED,或套接字上存在錯誤。
圖 7. 用于 connect 系統(tǒng)調(diào)用的調(diào)用序列
3 向 TCP 握手 圖 8、圖 9 和圖 10 顯示了客戶端發(fā)布 connect 和服務(wù)器發(fā)布 accept 以指示和建立 TCP 連接時的調(diào)用序列。 圖 8. 用于 SYN 包的流序列
當(dāng)客戶端發(fā)布 connect 時,在協(xié)議層調(diào)用 tcp_output() 函數(shù),將 SYN 包輸出到接口。如圖 9 所示,soconnect 現(xiàn)在返回到connect() 函數(shù),并進(jìn)入睡眠狀態(tài)。客戶端上的套接字狀態(tài)現(xiàn)在是 SYN_SENT。接口層調(diào)用 if_output() (實(shí)際上是接口特定的輸出函數(shù)),將包發(fā)送到 n/w。 目的地(服務(wù)器)上的接口接收傳入 SYN 包,將其放在 ipintrq 隊列中,并引發(fā)軟件中斷。包然后由調(diào)用 tcp_input 例程的ipintr() 獲取。tcp_input() 在 s/w 中斷時執(zhí)行,并從 ipintrq 拾取 SYN 包,對其進(jìn)行處理,并將部分完成的套接字連接放入完成的套接字隊列。服務(wù)器端的套接字狀態(tài)現(xiàn)在是 SYN_RCVD。每次處理后,tcp_input() 例程都調(diào)用 tcp_output() (如果需要將響應(yīng)套接字發(fā)送到另一端)。 圖 9. 用于 SYN ACK 包的流序列
處理 SYN 后,服務(wù)器使用 tcp_output () 、ip_output () 和 if_output () 序列發(fā)送 SYN ACK 包。客戶端上的 n/w 接口接收此包,將其放在 ipintrq 中,并引發(fā) s/w 中斷。同樣,ipintr () 從 ipintrq 獲取該包,并將其傳遞到客戶端 TCP 堆棧上的tcp_input () 例程。包現(xiàn)在是經(jīng)過處理的,并調(diào)用了 soisconnected () ,它喚醒連接調(diào)用??蛻舳松系奶捉幼譅顟B(tài)現(xiàn)在已建立。 圖 10. 用于 ACK 包的流序列
客戶端上的 tcp_input () 例程處理 SYN ACK 包,并調(diào)用 tcp_output () 將 ACK 包發(fā)回到服務(wù)器。服務(wù)器端上的 tcp_input () 處理此 ACK 包,并調(diào)用 soisconnected () 。此函數(shù)從未完成的套接字隊列移除套接字,并將其放入完成的套接字隊列,然后調(diào)用 Wakeup () ,以喚醒 accept 調(diào)用。服務(wù)器端的套接字現(xiàn)在已建立。 Shutdown shutdown (struct proc *p, struct shutdown_args *uap, int *retval);
Struct shutdown_args
{
int s;
int how;
}
|
在 shutdown 系統(tǒng)調(diào)用中: s 是套接字描述符。how 指定將關(guān)閉哪一部分連接。how 的值 0、1 和 2 分別指定關(guān)閉連接的讀取部分、寫入部分和同時關(guān)閉連接的讀取及寫入部分。
shutdown 系統(tǒng)調(diào)用關(guān)閉連接的任意一端或兩端。如果需要關(guān)閉讀取部分,則會丟棄接收緩沖區(qū)中存在的任何數(shù)據(jù),并關(guān)閉該端的連接。對寫入部分,TCP 發(fā)送任何剩余的數(shù)據(jù),然后終止連接的寫入端。
圖 11. 用于 shutdown 系統(tǒng)調(diào)用的調(diào)用序列
如果需要關(guān)閉連接的讀取部分,則 soshutdown() 函數(shù)調(diào)用 sorflush() 。sorflush() 標(biāo)記套接字以拒絕任何傳入的包,并釋放保存的任何系統(tǒng)資源。 如果需要關(guān)閉連接的寫入部分,則調(diào)用 tcp_usrreq() ,并將 PRU_SHUTDOWN 作為請求。PRU_SHUTDOWN 的切換實(shí)例根據(jù)當(dāng)前的狀態(tài)調(diào)用 tcp_usrclosed() 函數(shù),以更新套接字的狀態(tài)。TCP/IP 狀態(tài)圖表可以幫助了解套接字在任何給定的時間存在的不同狀態(tài)。如果從 tcp_usrclosed() 返回時需要發(fā)送 FIN,則調(diào)用 tcp_output() 將其發(fā)送到接口。 Close soo_close(struct file *fp , struct proc *p); |
在 close 系統(tǒng)調(diào)用中: fp 是指向文件結(jié)構(gòu)的指針。p 是一個指向調(diào)用進(jìn)程的 proc 結(jié)構(gòu)的指針。
close 系統(tǒng)調(diào)用可關(guān)閉或中止套接字上任何掛起的連接。
soo_close() 僅調(diào)用 so_close() 函數(shù),該函數(shù)首先檢查要關(guān)閉的套接字是否為偵聽套接字(正在接收傳入連接的套接字)。如果是,則遍歷兩個套接字隊列,以檢查任何掛起的連接。對每個掛起的連接,將調(diào)用 soabort() 以發(fā)布 tcp_usrreq() ,并將 PRU_ABORT 用作請求。此切換實(shí)例調(diào)用 tcp_drop() 以檢查套接字的狀態(tài)。
如果狀態(tài)是 SYN_RCVD,則通過將狀態(tài)設(shè)置為 CLOSED 并調(diào)用 tcp_output() 發(fā)送 RST 段。tcp_close() 函數(shù)然后關(guān)閉套接字。tcp_close 函數(shù)更新路由度量結(jié)構(gòu)的三個變量,然后釋放套接字持有的資源。 如果套接字不是偵聽套接字,則控制開始使用 soclose() ,以檢查是否已存在附加到套接字的控制塊。如果不存在,則 sofree() 釋放套接字。如果存在,則調(diào)用具有 PRU_DETACH 的 tcp_usrreq() 將協(xié)議與套接字分離。PRU_DETACH 的切換實(shí)例調(diào)用tcp_disconnect() ,以檢查連接狀態(tài)是否為 ESTABLISHED。如果不是,則 tcp_disconnect() 調(diào)用 tcp_close() ,以釋放 Internet 和控制塊。否則,tcp_disconnect() 檢查延遲時間和延遲套接字選項。如果設(shè)置了該選項,并且延遲時間為零,則調(diào)用tcp_drop() 。如果未設(shè)置,則調(diào)用 tcp_usrclosed() ,以設(shè)置套接字的狀態(tài),并調(diào)用 tcp_output() (如果需要發(fā)送 FIN 段)。 圖 12 顯示了 TCP 應(yīng)用程序發(fā)布 close 系統(tǒng)調(diào)用時發(fā)生的重要調(diào)用。 圖 12. 用于 close 系統(tǒng)調(diào)用的調(diào)用序列
Send sendmsg ( struct proc*p, struct sendmsg_args *uap, int retval);
struct sendmsg_args
{
int s;
caddr_t msg;
int flags;
};
|
在 send 系統(tǒng)調(diào)用中: s 是套接字描述符。msg 是指向 msghdr 結(jié)構(gòu)的指針。flags 是控制信息。
n/w 接口上有四個要發(fā)送數(shù)據(jù)的系統(tǒng)調(diào)用:write 、writev 、sendto 和 sendmsg 。本文僅討論 sendmsg() 系統(tǒng)調(diào)用。所有的四個調(diào)用最終調(diào)用 sosend() 。盡管 send (進(jìn)程調(diào)用的庫函數(shù))、sendto 和 sendmsg 系統(tǒng)調(diào)用僅可以對套接字描述符操作,但write 和 writev 系統(tǒng)調(diào)用則可以對任何類型的描述符操作。 圖 13. 用于 sendmsg 的調(diào)用序列
sendmsg 系統(tǒng)調(diào)用將從進(jìn)程發(fā)送的消息復(fù)制到內(nèi)核空間,并調(diào)用 sendit() 。在 sendit() 中,將初始化一個結(jié)構(gòu),以便從進(jìn)程將輸出收集到內(nèi)核中的內(nèi)存緩沖區(qū)。還可以將地址和控制信息從進(jìn)程復(fù)制到內(nèi)核,然后調(diào)用 sosend() ,以執(zhí)行以下四項任務(wù):
- 基于
sendit() 函數(shù)傳遞的值初始化各種參數(shù)。 - 驗(yàn)證套接字的條件和連接的狀態(tài),并確定傳遞消息和報告錯誤所需的空間。
- 分配內(nèi)存并從進(jìn)程復(fù)制數(shù)據(jù)。
- 使協(xié)議特定的調(diào)用將數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)。
然后調(diào)用 tcp_usrreq() ,并根據(jù)進(jìn)程指定的標(biāo)志,控制切換到 PRU_SEND 或 PRU_SENDOOB(以發(fā)送帶區(qū)外數(shù)據(jù))。對于 PRU_SENDOOB,發(fā)送緩沖區(qū)大小可以超過 512 字節(jié),將釋放任何分配的內(nèi)存并中斷控制。否則,sbappend() 和 tcp_output() 函數(shù)由 PRU_SEND 和 PRU_SENDOOB 調(diào)用。sbappend() 在發(fā)送緩沖區(qū)的末尾添加數(shù)據(jù),并且 tcp_output() 將該段發(fā)送到接口。 Receive recvmsg(struct proc *p, struct recvmsg_args *uap , int *retval);
struct recvmsg_args
{
int s,
struct msghdr *msg,
int flags,
};
|
在 receive 系統(tǒng)調(diào)用中: s 是套接字描述符。msg 是指向 msghdr 結(jié)構(gòu)的指針。flags 指定控制信息。
有四個系統(tǒng)調(diào)用可以用于從連接接收數(shù)據(jù):read 、readv 、recvfrom 和 recvmsg 。盡管 recv (進(jìn)程使用的庫函數(shù))、recvfrom 和 recvmsg 僅可以對套接字描述符操作,但 read 和 readv 可以對任何種類的描述符操作。所有的 read 系統(tǒng)調(diào)用最終調(diào)用soreceive() 。 圖 14 顯示了用于 recvmsg 系統(tǒng)調(diào)用的調(diào)用序列。recvmsg() 和 recvit() 函數(shù)初始化各種數(shù)組和結(jié)構(gòu),將接收的數(shù)據(jù)從內(nèi)核發(fā)送到進(jìn)程。recvit() 調(diào)用 soreceive() ,以便將接收的數(shù)據(jù)從套接字緩沖區(qū)傳輸?shù)浇邮站彌_區(qū)進(jìn)程。soreceive() 函數(shù)執(zhí)行各種檢查,如: - 是否設(shè)置了 MSG_OOB 標(biāo)志。
- 進(jìn)程是否嘗試接收數(shù)據(jù)。
- 是否應(yīng)該阻塞,直到足夠的數(shù)據(jù)到達(dá)。
- 將讀取數(shù)據(jù)傳輸?shù)竭M(jìn)程。
- 檢查數(shù)據(jù)是帶區(qū)外數(shù)據(jù)還是常規(guī)數(shù)據(jù),并進(jìn)行相應(yīng)的處理。
- 當(dāng)數(shù)據(jù)接收完成后通知協(xié)議。
圖 14. 用于 recvmsg 的調(diào)用序列
 當(dāng)設(shè)置 MSG_OOB 標(biāo)志時或數(shù)據(jù)接收完成后,soreceive() 函數(shù)進(jìn)行與協(xié)議相關(guān)的請求。在接收帶區(qū)外數(shù)據(jù)的情況下,協(xié)議層檢查不同的條件,以驗(yàn)證接收的數(shù)據(jù)是否為帶區(qū)外數(shù)據(jù),然后將其返回到套接字層。在后一種情況中,協(xié)議層調(diào)用 tcp_output() ,將窗口更新段發(fā)送到網(wǎng)絡(luò)。它通知另一端任何空間都可用于接收數(shù)據(jù)。 結(jié)束語 在本文中,您學(xué)習(xí)了觸發(fā)低級別調(diào)用以完成某些任務(wù)的最重要的 TCP 函數(shù)調(diào)用。圖中的調(diào)用序列顯示了內(nèi)核級 TCP 調(diào)用的簡要概述。本文是了解 FreeBSD TCP/IP 堆棧組織的很好起點(diǎn)。
|