偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Linux高性能網(wǎng)絡編程十談 | TCP底層的收發(fā)過程

系統(tǒng) Linux
談完上一篇《Linux高性能網(wǎng)絡編程十談|網(wǎng)絡篇》,我們繼續(xù)探索高性能網(wǎng)絡編程,但是我覺得在談系統(tǒng)API之前可以先講一些Linux底層的收發(fā)包過程,如下這是一個簡單的socket編程代碼:

談完上一篇《Linux高性能網(wǎng)絡編程十談|網(wǎng)絡篇》,我們繼續(xù)探索高性能網(wǎng)絡編程,但是我覺得在談系統(tǒng)API之前可以先講一些Linux底層的收發(fā)包過程,如下這是一個簡單的socket編程代碼:

int main() {
    ... 

    fd = socket(AF_INET, SOCKET_STREAM, 0);
    bind(fd, ...);
    listen(fd, ...);

    // 如何建立連接
    ...
    afd = accept(fd, ...);

    // 如何接收數(shù)據(jù)
    ...
    read(afd, ...);

    // 如何發(fā)送數(shù)據(jù)
    ...
    send(afd, ...);

    // 如何關閉連接
    ...
    close(fd);
    ...
}

第一部分:如何建立連接

從上一篇文章我們介紹了網(wǎng)絡協(xié)議,我們知道TCP/IP協(xié)議族劃分了應用層、TCP傳輸層、IP網(wǎng)絡層、鏈路層(以太層驅動)。

如上圖看應用層,通常在網(wǎng)絡編程中我們需要調用accept的API建立TCP連接,那TCP如何做的呢?

從上圖的流程可以看到:

(1)client端發(fā)起TCP握手,發(fā)送syn包;

(2)內(nèi)核收到包以后先將當前連接的信息插入到網(wǎng)絡的SYN隊列;

(3)插入成功后會返回握手確認(SYN+ACK);

(4)client端如果繼續(xù)完成TCP握手,回復ACK確認;

(5)內(nèi)核會將TCP握手完成的包,先將對應的連接信息從SYN隊列取出;

(6)將連接信息丟入到ACCEPT隊列;

(7)應用層sever通過系統(tǒng)調用accept就能拿到這個連接,整個網(wǎng)絡套接字連接完成;

那基于這個圖,我想問問讀者這里會有什么問題么?

細心的讀者應該可以看出:

1、這里有兩個隊列,必然會有滿的情況,那如果遇到這種情況內(nèi)核是怎么處理的呢?

(1)如果SYN隊列滿了,內(nèi)核就會丟棄連接;

(2)如果ACCEPT隊列滿了,那內(nèi)核不會繼續(xù)將SYN隊列的連接丟到ACCEPT隊列,如果SYN隊列足夠大,client端后續(xù)收發(fā)包就會超時;

(3)如果SYN隊列滿了,就會和(1)一樣丟棄連接;

2、如何控制SYN隊列和ACCEPT隊列的大小?

(1)內(nèi)核2.2版本之前通過listen的backlog可以設置SYN隊列(半連接狀態(tài)SYN_REVD)和ACCEPT隊列(完全連接狀態(tài)ESTABLISHED)的上限;

(2)內(nèi)核2.2版本以后backlog只是表示ACCEPT隊列上限,SYN隊列的上限可以通過/proc/sys/net/ipv4/tcp_max_syn_backlog設置;

3、server端通過accept一直等,豈不是會卡住收包的線程?

在linux網(wǎng)絡編程中我們都會追求高性能,accept如果卡住接收線程,性能會上不去,所以socket編程中就會有阻塞和非阻塞模式。

(1)阻塞模式下的accept就會卡住,當前線程什么事情都干不了;

(2)非阻塞模式下,可以通過輪詢accept去處理其他的事情,如果返回EAGAIN,就是ACCEPT隊列為空,如果返回連接信息,就是可以處理當前連接;

第二部分:接收數(shù)據(jù)

(1)當網(wǎng)卡接收到報文并判斷為TCP協(xié)議后,將會調用到內(nèi)核的tcp_v4_rcv方法,如果數(shù)據(jù)按順序收到S1數(shù)據(jù)包,則直接插入receive隊列中;

(2)當收到了S3數(shù)據(jù)包,在第1步結束后,應該收到S2序號,但是報文是亂序進來的,則將S3插入out_of_order隊列(這個隊列存儲亂序報文);

(3)接下來收到S2數(shù)據(jù)包,如第1步直接進入receive隊列,由于此時out_of_order隊列不像第1步是空的,所以引發(fā)了接來的第4步;

(4)每次向receive隊列插入報文時都會檢查out_of_order隊列,如果遇到期待的序號S3,則從out_of_order隊列摘除,寫入到receive隊列;

(5)現(xiàn)在應用程序開始調用recv方法;

(6)經(jīng)過層層封裝調用,接收TCP消息最終會走到tcp_recvmsg方法;

(7)現(xiàn)在需要拷貝數(shù)據(jù)從內(nèi)核態(tài)到用戶態(tài),如果receive隊列為空,會先檢查SO_RCVLOWAT這個閥值(0表示收到指定的數(shù)據(jù)返回,1表示只要讀取到數(shù)據(jù)就返回,系統(tǒng)默認是1),如果已經(jīng)拷貝的字節(jié)數(shù)到現(xiàn)在還小于它,那么可能導致進程會休眠,等待拷貝更多的數(shù)據(jù);

(8)將數(shù)據(jù)從內(nèi)核態(tài)拷貝到用戶態(tài),recv返回拷貝數(shù)據(jù)的大小;

(9)為了選擇降低網(wǎng)絡包延時或者提升吞吐量,系統(tǒng)提供了tcp_low_latency參數(shù),如果為0值,用戶暫時沒有讀數(shù)據(jù)則數(shù)據(jù)包進入prequeue隊列,提升吞吐量,否則不使用prequeue隊列,進入tcp_v4_do_rcv,降低延時;

第三部分:發(fā)送數(shù)據(jù)

(1)假設調用send方法來發(fā)送大于一個MSS(比如2K)的數(shù)據(jù);

(2)內(nèi)核調用tcp_sendmsg,實現(xiàn)復制數(shù)據(jù),寫入隊列和組裝tcp協(xié)議頭;

(3)在調用tcp_sendmsg先需要在內(nèi)核獲取skb,將用戶態(tài)數(shù)據(jù)拷貝到內(nèi)核態(tài),內(nèi)核真正執(zhí)行報文的發(fā)送,與send方法的調用并不是同步的,即send方法返回成功,也不一定把IP報文都發(fā)送到網(wǎng)絡中了。因此,需要把用戶需要發(fā)送的用戶態(tài)內(nèi)存中的數(shù)據(jù),拷貝到內(nèi)核態(tài)內(nèi)存中,不依賴于用戶態(tài)內(nèi)存,也使得進程可以快速釋放發(fā)送數(shù)據(jù)占用的用戶態(tài)內(nèi)存。但這個拷貝操作并不是簡單的復制,而是把待發(fā)送數(shù)據(jù),按照MSS來劃分成多個盡量達到MSS大小的分片報文段,復制到內(nèi)核中的sk_buff結構來存放;

(4)將數(shù)據(jù)拷貝到發(fā)送隊列中tcp_write_queue;

(5)調用tcp_push發(fā)送數(shù)據(jù)到IP層,這里主要滑動窗口,慢啟動,擁塞窗口的控制和判斷是否使用Nagle算法合并小報文(上一篇已經(jīng)有介紹);

(6)組裝IP報文頭,通過經(jīng)過iptables或者tcpdump等netfilter模塊過濾,將數(shù)據(jù)交給鄰居子系統(tǒng)(主要功能是查找需要發(fā)送的MAC地址,發(fā)送arp請求,封裝MAC頭等);

(7)調用網(wǎng)卡驅動程序將數(shù)據(jù)發(fā)送出去;

第四部分:關閉連接

關閉連接就是TCP揮手過程,我們都知道TCP連接是一種可靠的連接,那如何才能完整可靠的完成關閉連接呢?linux系統(tǒng)提供了兩個函數(shù):

close對應tcp_close方法,通過減少socket的引用次數(shù)實現(xiàn)關閉,僅當引用計數(shù)為0時才會觸發(fā)tcp_close;

shutdown對應tcp_shutdown方法,不關心socket被引用次數(shù),直接關閉對應的連接;

(1)shutdown可攜帶一個參數(shù),取值有3個,分別意味著:只關閉讀、只關閉寫、同時關閉讀寫;

(2)若shutdown的是半打開的連接,則發(fā)出RST來關閉連接;

(3)若shutdown的是正常連接,那么關閉讀其實與對端是沒有關系的;

(4)若參數(shù)中有標志位為關閉寫,那么下面做的事與close是一致的,發(fā)出FIN包,告訴對方本機不會再發(fā)消息了;

第五部分:思考題

基于本文留幾個思考題,下一篇文章解答。

(1)發(fā)送方法返回成功后,數(shù)據(jù)一定發(fā)送到了TCP的對端么?

(調用了IP層的方法返回后,也未必就保證此時數(shù)據(jù)一定發(fā)送成功)

(2)1個socket套接字可能被多個進程在使用,出現(xiàn)并發(fā)訪問時,內(nèi)核是怎么處理這種狀況的?

(3)若socket為默認的阻塞套接字,調用recv方法傳入的len參數(shù),如果網(wǎng)絡包的數(shù)據(jù)小于len,recv會返回么?

(4)當socket被多進程或者多線程共享時,關閉連接時有何區(qū)別?

責任編輯:華軒 來源: 周末程序猿
相關推薦

2024-03-18 13:43:20

Linux架構

2023-11-01 11:59:13

2023-11-01 10:38:46

Linux高性能網(wǎng)絡編程

2023-11-01 10:58:31

系統(tǒng)調用高性能網(wǎng)絡編程Linux

2023-11-01 11:40:46

Linux高性能網(wǎng)絡編程工具

2023-11-01 11:27:10

Linux協(xié)程

2023-11-01 11:51:08

Linux性能優(yōu)化

2023-11-01 11:07:05

Linux高性能網(wǎng)絡編程線程

2023-11-01 11:20:57

2023-11-01 11:13:58

Linux信號處理定時器

2025-06-26 01:27:00

2020-11-06 18:51:17

LinuxTCP服務器

2024-08-06 08:22:18

2024-10-06 14:37:52

2024-09-03 09:15:37

2024-10-16 11:03:30

Linux高性能編程

2012-09-25 15:02:50

C#網(wǎng)絡協(xié)議

2021-02-06 09:40:11

LinuxCPU高性能

2022-03-21 14:13:22

Go語言編程

2023-04-14 14:35:35

網(wǎng)絡
點贊
收藏

51CTO技術棧公眾號