亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

搞了半天,終于弄懂了TCP Socket數(shù)據(jù)的接收和發(fā)送,太難

 

本文將從上層介紹linux上的TCP/IP棧是如何工作的,特別是socket系統(tǒng)調(diào)用和內(nèi)核數(shù)據(jù)結(jié)構(gòu)的交互、內(nèi)核和實(shí)際網(wǎng)絡(luò)的交互。寫這篇文章的部分原因是解釋監(jiān)聽隊(duì)列溢出(listen queue overflow)是如何工作的,因?yàn)樗c我工作中一直在研究的一個(gè)問題相關(guān)。

建好的連接怎么工作

先從建好的連接開始介紹,稍后將解釋新建連接是如何工作的。

內(nèi)核管理的每一個(gè)TCP文件描述符都是一個(gè)struct, 它記錄TCP相關(guān)的信息(如序列號(hào)、當(dāng)前窗口大小等等),以及一個(gè)接收緩沖區(qū)(receive buffer,或者叫receive queue)和一個(gè)寫緩沖區(qū)(write buffer,或者叫write queue),后面我會(huì)交替使用術(shù)語buffer和queue。如果你對(duì)更多細(xì)節(jié)感興趣,可以在Linux內(nèi)核的net/sock.h中看到socket結(jié)構(gòu)的實(shí)現(xiàn)。

當(dāng)一個(gè)新的數(shù)據(jù)包進(jìn)入網(wǎng)絡(luò)接口(NIC)時(shí),通過被NIC中斷或通過輪詢NIC的方式通知內(nèi)核獲取數(shù)據(jù)。通常內(nèi)核是由中斷驅(qū)動(dòng)還是處于輪詢模式取決于網(wǎng)絡(luò)通信量;當(dāng)NIC非常繁忙時(shí),內(nèi)核輪詢效率更高,但如果NIC不繁忙,則可以使用中斷來節(jié)省CPU周期和電源。Linux稱這種技術(shù)為NAPI,字面意思是“新的api”。

當(dāng)內(nèi)核從NIC獲取數(shù)據(jù)包時(shí),它會(huì)對(duì)數(shù)據(jù)包進(jìn)行解碼,并根據(jù)源IP、源端口、目標(biāo)IP和目標(biāo)端口找出與該數(shù)據(jù)包相關(guān)聯(lián)的TCP連接。此信息用于查找與該連接關(guān)聯(lián)的內(nèi)存中的struct sock。假設(shè)數(shù)據(jù)包是按順序的到來的,那么數(shù)據(jù)有效負(fù)載就被復(fù)制到套接字的接收緩沖區(qū)中。此時(shí),內(nèi)核將執(zhí)行read(2)或使用諸如select(2)或epoll_wait(2)等I/O多路復(fù)用方式系統(tǒng)調(diào)用,喚醒等待此套接字的進(jìn)程。

當(dāng)用戶態(tài)的進(jìn)程實(shí)際調(diào)用文件描述符上的read(2)時(shí),它會(huì)導(dǎo)致內(nèi)核從其接收緩沖區(qū)中刪除數(shù)據(jù),并將該數(shù)據(jù)復(fù)制到此進(jìn)程調(diào)用read(2)所提供的緩沖區(qū)中。

發(fā)送數(shù)據(jù)的工作原理類似。當(dāng)應(yīng)用程序調(diào)用write(2)時(shí),它將數(shù)據(jù)從用戶提供的緩沖區(qū)復(fù)制到內(nèi)核寫入隊(duì)列中。隨后,內(nèi)核將把數(shù)據(jù)從寫隊(duì)列復(fù)制到NIC中,并實(shí)際發(fā)送數(shù)據(jù)。如果網(wǎng)絡(luò)繁忙,如果TCP發(fā)送窗口已滿,或者如果有流量整形策略等等,從用戶實(shí)際調(diào)用write(2)開始,到向NIC傳輸數(shù)據(jù)的實(shí)際時(shí)間可能會(huì)有所延遲。

這種設(shè)計(jì)的一個(gè)結(jié)果是,如果應(yīng)用程序讀取速度太慢或?qū)懭胨俣忍欤瑑?nèi)核的接收和寫入隊(duì)列可能會(huì)被填滿。因此,內(nèi)核為讀寫隊(duì)列設(shè)置最大大小。這樣可以確保行為不可控的應(yīng)用程序使用有限制的內(nèi)存量。例如,內(nèi)核可能會(huì)將每個(gè)接收和寫入隊(duì)列的大小限制在100KB。然后每個(gè)TCP套接字可以使用的最大內(nèi)核內(nèi)存量大約為200KB(因?yàn)榕c隊(duì)列的大小相比,其他TCP數(shù)據(jù)結(jié)構(gòu)的大小可以忽略不計(jì))。

讀語義

如果接收緩沖區(qū)為空,并且用戶調(diào)用read(2),則系統(tǒng)調(diào)用將被阻塞,直到數(shù)據(jù)可用。

如果接收緩沖區(qū)是非空的,并且用戶調(diào)用read(2),系統(tǒng)調(diào)用將立即返回這些可用的數(shù)據(jù)。如果讀取隊(duì)列中準(zhǔn)備好的數(shù)據(jù)量小于用戶提供的緩沖區(qū)的大小,則可能發(fā)生部分讀取。調(diào)用方可以通過檢查read(2)的返回值來檢測到這一點(diǎn)。

如果接收緩沖區(qū)已滿,而TCP連接的另一端嘗試發(fā)送更多的數(shù)據(jù),內(nèi)核將拒絕對(duì)數(shù)據(jù)包進(jìn)行ACK。這只是常規(guī)的TCP擁塞控制。

寫語義

如果寫入隊(duì)列未滿,并且用戶調(diào)用寫入,則系統(tǒng)調(diào)用將成功。如果寫入隊(duì)列有足夠的空間,則將復(fù)制所有數(shù)據(jù)。如果寫入隊(duì)列只有部分?jǐn)?shù)據(jù)的空間,那么將發(fā)生部分寫入,并且只有部分?jǐn)?shù)據(jù)將被復(fù)制到緩沖區(qū)。調(diào)用方通過檢查write(2)的返回值來檢查這一點(diǎn)。

如果寫入隊(duì)列已滿,并且用戶調(diào)用寫入write(2)),則系統(tǒng)調(diào)用將被阻塞。

新建連接的工作機(jī)制

在上一節(jié)中,我們看到了已建立的連接如何使用接收和寫入隊(duì)列來限制為每個(gè)連接分配的內(nèi)核內(nèi)存量。使用類似的技術(shù)也用來限制為新連接保留的內(nèi)核內(nèi)存量。

從用戶態(tài)的角度來看,新建立的TCP連接是通過在監(jiān)聽套接字上調(diào)用accept(2)來創(chuàng)建的。監(jiān)聽套接字是使用listen(2)系統(tǒng)調(diào)用的套接字。

accept(2)的原型采用一個(gè)套接字和兩個(gè)字段來存儲(chǔ)另一端套接字的信息。accept(2)返回的值是一個(gè)整數(shù),表示新建立連接的文件描述符:

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

listen(2)的原型采用了一個(gè)套接字文件描述符和一個(gè)backlog參數(shù):

int listen(int sockfd, int backlog);

backlog是一個(gè)參數(shù),當(dāng)用戶沒有足夠快地調(diào)用accept(2)時(shí),它控制內(nèi)核將為新連接保留多少內(nèi)存。

例如,假設(shè)您有一個(gè)阻塞的單線程HTTP服務(wù)器,每個(gè)HTTP請(qǐng)求大約需要100毫秒。在這種情況下,HTTP服務(wù)器將花費(fèi)100毫秒處理每個(gè)請(qǐng)求,然后才能再次調(diào)用accept(2)。這意味著在最多10個(gè) rps 的情況下不會(huì)有排隊(duì)現(xiàn)象。如果內(nèi)核中有10個(gè)以上的 rps,則有兩個(gè)選擇。

內(nèi)核的第一個(gè)選擇是根本不接受連接。例如,內(nèi)核可以拒絕對(duì)傳入的SYN包進(jìn)行ACK。更常見的情況是,內(nèi)核將完成TCP三次握手,然后使用RST終止連接。不管怎樣,結(jié)果都是一樣的:如果連接被拒絕,就不需要分配接收或?qū)懭刖彌_區(qū)。這樣做的理由是,如果用戶空間進(jìn)程沒有足夠快地接受連接,那么正確的做法是使新請(qǐng)求失敗。反對(duì)這樣做的理由是,這太粗暴(aggressive),尤其是如果新的連接爆發(fā)(bursty)的時(shí)候。

內(nèi)核的第二個(gè)選擇是接受連接并為其分配一個(gè)套接字結(jié)構(gòu)(包括接收/寫入緩沖區(qū)),然后將套接字對(duì)象排隊(duì)以備以后使用。下次用戶調(diào)用accept(2)將立即獲得已分配的套接字, 而不是阻塞系統(tǒng)調(diào)用。

支持第二種方式的理由是,當(dāng)處理速率或連接速率趨向于爆發(fā)時(shí),它過于“寬宏大量”。例如,在我們剛才描述的服務(wù)器中,假設(shè)有10個(gè)新連接同時(shí)出現(xiàn),然后這一秒中沒有更多的連接出現(xiàn)。如果內(nèi)核將新連接排隊(duì),那么在第這一秒中所有的請(qǐng)求都會(huì)被處理。如果內(nèi)核采用拒絕新的連接的策略,那么即使進(jìn)程本來能夠滿足請(qǐng)求速率的,也只有一個(gè)連接會(huì)成功。

不過有兩個(gè)反對(duì)排隊(duì)的論點(diǎn)。第一個(gè)問題是,過多的排隊(duì)會(huì)導(dǎo)致分配大量的內(nèi)核內(nèi)存。如果內(nèi)核正在分配帶有大接收緩沖區(qū)的數(shù)千個(gè)套接字,那么內(nèi)存使用量可能會(huì)快速增長,而用戶空間進(jìn)程甚至可能無法處理所有這些請(qǐng)求。另一個(gè)反對(duì)排隊(duì)的論點(diǎn)是,它使應(yīng)用程序在連接的另一端(客戶機(jī))看起來很慢。客戶機(jī)將看到它可以建立新的TCP連接,但是當(dāng)它嘗試使用它們時(shí),服務(wù)器似乎響應(yīng)非常慢。所以建議在這種情況下,最好是讓新的連接失敗,因?yàn)檫@樣可以提供更明顯的服務(wù)器不正常的反饋。此外,如果服務(wù)器嚴(yán)重破壞了新的連接,客戶機(jī)就可以知道要退讓(back off);這是另一種擁塞控制形式。

監(jiān)聽隊(duì)列(listen queue)和溢出

正如您可能懷疑的那樣,內(nèi)核實(shí)際上結(jié)合了這兩種方法。內(nèi)核將會(huì)對(duì)新連接進(jìn)行排隊(duì),但只是一定數(shù)量的連接。內(nèi)核將排隊(duì)的連接數(shù)量由listen(2)的backlog參數(shù)控制。通常此值設(shè)置為相對(duì)較小的值。在Linux上,socket.h 將 somaxconn 的值設(shè)置為128,在kernel 2.4.25之前,這是允許的最大值。現(xiàn)在最大值是在/proc/sys/net/core/somaxconn中指定的,但是通常您會(huì)發(fā)現(xiàn)程序使用somaxconn(或更小的硬編碼值)。

當(dāng)監(jiān)聽隊(duì)列填滿時(shí),新連接會(huì)被拒絕。這稱為監(jiān)聽隊(duì)列溢出。您可以通過讀取/proc/net/netstat并檢查ListenOverflows的值來觀察情況。這是整個(gè)內(nèi)核的全局計(jì)數(shù)器。據(jù)我所知,您無法獲得每個(gè)監(jiān)聽套接字的監(jiān)聽溢出統(tǒng)計(jì)信息。

在編寫網(wǎng)絡(luò)服務(wù)器時(shí),監(jiān)控監(jiān)聽溢出非常重要,因?yàn)楸O(jiān)聽溢出不會(huì)從服務(wù)器的角度觸發(fā)任何用戶可見的行為。服務(wù)器將愉快地accept(2)每日的連接,而不返回任何連接被丟棄的跡象。例如,假設(shè)您為Python應(yīng)用程序使用Nginx作為代理服務(wù)器。

如果python應(yīng)用程序太慢,則可能導(dǎo)致nginx listen套接字溢出。當(dāng)發(fā)生這種情況時(shí),您將在nginx日志中看不到任何關(guān)于這一點(diǎn)的指示,您將一直看到200狀態(tài)代碼,像往常一樣。因此,如果您只是監(jiān)視應(yīng)用程序的HTTP狀態(tài)代碼,您將無法看到阻止請(qǐng)求轉(zhuǎn)發(fā)到應(yīng)用程序的TCP錯(cuò)誤。

分享到:
標(biāo)簽:TCP
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績?cè)u(píng)定