「linux」關(guān)于關(guān)閉一個(gè)還有沒發(fā)送數(shù)據(jù)完的TCP連接思考

tcp相關(guān)視頻解析:

tcp專題訓(xùn)練營(yíng)之深度解析tcp/ip協(xié)議棧

徒手實(shí)現(xiàn)網(wǎng)絡(luò)協(xié)議棧反璃,請(qǐng)準(zhǔn)備好環(huán)境墓陈,一起來寫代碼

linux后臺(tái)開發(fā)面試中tcpip,哪些容易被問到的

當(dāng) close 一個(gè) TCP 連接時(shí),如果還有沒發(fā)送完的數(shù)據(jù)在緩沖區(qū)中鹉梨,內(nèi)核會(huì)怎么處理絮缅?

當(dāng)時(shí)我認(rèn)為,因?yàn)殛P(guān)閉 TCP 連接會(huì)觸發(fā)四次揮手過程,而為了讓四次揮手能夠快速完成叁熔,應(yīng)該會(huì)把發(fā)送緩沖區(qū)的數(shù)據(jù)清空,然后發(fā)送四次揮手的數(shù)據(jù)包床牧。

帶著疑問荣回,我去查閱 Linux 源碼的實(shí)現(xiàn),下面就是關(guān)閉一個(gè) TCP 連接的過程戈咳。

關(guān)閉 TCP 連接過程

關(guān)閉一個(gè) TCP 連接可以使用?close()?系統(tǒng)調(diào)用心软,我們來分析一下當(dāng)調(diào)用?close()?關(guān)閉一個(gè) TCP 連接時(shí)會(huì)發(fā)生什么事情。

當(dāng)調(diào)用?close()?系統(tǒng)調(diào)用時(shí)著蛙,會(huì)觸發(fā)調(diào)用?sys_close()?內(nèi)核函數(shù)删铃,其實(shí)現(xiàn)如下:

asmlinkagelongsys_close(unsignedintfd){structfile*filp;structfiles_struct*files=current->files;...returnfilp_close(filp, files);? ? ...}

sys_close()?函數(shù)最終會(huì)調(diào)用?file_close()?函數(shù)來關(guān)閉文件(由于在 Linux 中 socket 是一種特殊的文件),我們接著分析?filp_close()?函數(shù)的實(shí)現(xiàn):

int filp_close(structfile*filp, fl_owner_t id){? ? ...? ? fput(filp);returnretval;}void fput(structfile* file){? ? ...if(atomic_dec_and_test(&file->f_count)) {? ? ? ? ...if(file->f_op && file->f_op->release)? ? ? ? ? ? file->f_op->release(inode, file);? ? ? ? ...? ? }}

可以看到踏堡,最終會(huì)調(diào)用文件系統(tǒng)對(duì)應(yīng)的?release()?方法來處理關(guān)閉操作猎唁。對(duì)于 socket 文件系統(tǒng),release()?方法對(duì)應(yīng)的是?sock_close()?函數(shù)顷蟆,而?sock_close()?函數(shù)最終會(huì)調(diào)用?sock_release()?函數(shù)诫隅,所以我們來看看?sock_release()?函數(shù)的實(shí)現(xiàn):

void sock_release(structsocket*sock){if(sock->ops)? ? ? ? sock->ops->release(sock);? ? ...}

sock_release()?函數(shù)也很簡(jiǎn)單,就是調(diào)用對(duì)應(yīng)?協(xié)議族?的?release()?方法帐偎,因?yàn)?Linux 的 socket 文件系統(tǒng)可以支持多種協(xié)議族逐纬,比如?INET、Unix Domain Socket削樊、Netlink?等豁生。而對(duì)應(yīng)?INET協(xié)議族(網(wǎng)絡(luò))?來說,這個(gè)?release()?方法對(duì)應(yīng)的是?inet_release()?函數(shù)漫贞,inet_release()?函數(shù)實(shí)現(xiàn)如下:

int inet_release(structsocket*sock){structsock*sk = sock->sk;if(sk) {? ? ? ? long timeout;? ? ? ? ...? ? ? ? timeout =0;if(sk->linger && !(current->flags & PF_EXITING))? ? ? ? ? ? timeout = sk->lingertime;? ? ? ? sock->sk = NULL;? ? ? ? sk->prot->close(sk, timeout);? ? }return(0);}

inet_release()?函數(shù)最終會(huì)調(diào)用對(duì)應(yīng)?傳輸層(TCP或者UDP)?的?close()?方法甸箱,對(duì)于?TCP協(xié)議來說,close()?方法對(duì)應(yīng)的是?tcp_close()?函數(shù)绕辖,tcp_close()?就是關(guān)閉 TCP 連接的最后站點(diǎn)摇肌。

【文章福利】需要C/C++ Linux服務(wù)器架構(gòu)師學(xué)習(xí)資料加群812855908(資料包括C/C++,Linux仪际,golang技術(shù),Nginx昵骤,ZeroMQ树碱,MySQL,Redis变秦,fastdfs成榜,MongoDB,ZK蹦玫,流媒體赎婚,CDN刘绣,P2P,K8S挣输,Docker纬凤,TCP/IP,協(xié)程撩嚼,DPDK停士,ffmpeg等)

由于?tcp_close()?函數(shù)比較復(fù)雜,我們這里只分析當(dāng)發(fā)生緩沖區(qū)還有數(shù)據(jù)的情況下完丽,內(nèi)核會(huì)怎么處理緩沖區(qū)的數(shù)據(jù)恋技。

void tcp_close(structsock*sk, long timeout){structsk_buff*skb;? ? int data_was_unread =0;? ? ...// 如果接收緩沖區(qū)有數(shù)據(jù), 那么先情況接收緩沖區(qū)的數(shù)據(jù)while((skb= __skb_dequeue(&sk->receive_queue)) != NULL) {u32len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq - skb->h.th->fin;? ? ? ? data_was_unread += len;? ? ? ? __kfree_skb(skb);? ? }? ? ...if(data_was_unread !=0) {// 如果接收緩沖區(qū)有數(shù)據(jù)沒有處理tcp_set_state(sk, TCP_CLOSE);// 把socket狀態(tài)設(shè)置為TCP_CLOSEtcp_send_active_reset(sk, GFP_KERNEL);// 發(fā)送一個(gè)reset包給對(duì)端連接}elseif(sk->linger && sk->lingertime==0) {? ? ? ? ...? ? }elseif(tcp_close_state(sk)) {? ? ? ? tcp_send_fin(sk);// 開始發(fā)生四次揮手包}? ? ...}

從?tcp_close()?函數(shù)的實(shí)現(xiàn)可以看出,關(guān)閉過程主要有兩種情況:

如果接收緩沖區(qū)還有數(shù)據(jù)沒有被用戶處理逻族,那么就先把接收緩沖區(qū)的數(shù)據(jù)清空蜻底,并且發(fā)送一個(gè) reset 包給對(duì)端連接。

如果接收緩沖區(qū)沒有數(shù)據(jù)聘鳞,那么就調(diào)用?tcp_send_fin()?函數(shù)開始進(jìn)行四次揮手過程薄辅。

四次揮手過程如下圖:

接下來,我們分析?tcp_send_fin()?函數(shù)的實(shí)現(xiàn):

void tcp_send_fin(structsock*sk){structtcp_opt*tp = &(sk->tp_pinfo.af_tcp);structsk_buff*skb = skb_peek_tail(&sk->write_queue);// 發(fā)送緩沖區(qū)列表最后一個(gè)緩沖塊unsigned int mss_now;? ? ...if(tp->send_head != NULL) {// 如果發(fā)送緩沖區(qū)不為空TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_FIN;// 把最后一個(gè)發(fā)送緩沖塊設(shè)置FIN標(biāo)志TCP_SKB_CB(skb)->end_seq++;? ? ? ? tp->write_seq++;? ? }else{// 如果發(fā)送緩沖區(qū)為空for(;;) {? ? ? ? ? ? skb = alloc_skb(MAX_TCP_HEADER, GFP_KERNEL);// 申請(qǐng)一個(gè)新的緩沖塊if(skb)break;? ? ? ? ? ? current->policy |= SCHED_YIELD;? ? ? ? ? ? schedule();? ? ? ? }? ? ? ? skb_reserve(skb, MAX_TCP_HEADER);? ? ? ? skb->csum =0;? ? ? ? TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_FIN);// 設(shè)置FIN標(biāo)志TCP_SKB_CB(skb)->sacked =0;? ? ? ? TCP_SKB_CB(skb)->seq = tp->write_seq;? ? ? ? TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq +1;? ? ? ? tcp_send_skb(sk, skb,1, mss_now);// 發(fā)送給對(duì)端連接}? ? ...}

在?tcp_send_fin()?函數(shù)我們終于找到了當(dāng)發(fā)送緩沖區(qū)不為空的處理搁痛,當(dāng)發(fā)送緩沖區(qū)不為空時(shí)长搀,首先會(huì)獲取發(fā)送緩沖區(qū)的最后一個(gè)緩沖塊,然后把這個(gè)緩沖區(qū)的?FIN標(biāo)志位?設(shè)置上鸡典。

所以我前面的想法是錯(cuò)的源请,當(dāng)關(guān)閉一個(gè) TCP 連接時(shí),如果發(fā)送緩沖區(qū)還有數(shù)據(jù)沒發(fā)送完彻况,那么內(nèi)核只會(huì)把發(fā)送緩沖區(qū)最后一個(gè)緩沖塊設(shè)置上?FIN標(biāo)志谁尸,而不是把發(fā)送緩沖區(qū)清空。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末纽甘,一起剝皮案震驚了整個(gè)濱河市良蛮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌悍赢,老刑警劉巖决瞳,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異左权,居然都是意外死亡皮胡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門赏迟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屡贺,“玉大人,你說我怎么就攤上這事∷φ唬” “怎么了泻仙?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)量没。 經(jīng)常有香客問我玉转,道長(zhǎng),這世上最難降的妖魔是什么允蜈? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任冤吨,我火速辦了婚禮,結(jié)果婚禮上饶套,老公的妹妹穿的比我還像新娘漩蟆。我一直安慰自己,他們只是感情好妓蛮,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布怠李。 她就那樣靜靜地躺著,像睡著了一般蛤克。 火紅的嫁衣襯著肌膚如雪捺癞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天构挤,我揣著相機(jī)與錄音髓介,去河邊找鬼。 笑死筋现,一個(gè)胖子當(dāng)著我的面吹牛唐础,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播矾飞,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼一膨,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了洒沦?” 一聲冷哼從身側(cè)響起豹绪,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎申眼,沒想到半個(gè)月后瞒津,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡括尸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年仲智,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姻氨。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖剪验,靈堂內(nèi)的尸體忽然破棺而出肴焊,到底是詐尸還是另有隱情前联,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布娶眷,位于F島的核電站似嗤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏届宠。R本人自食惡果不足惜烁落,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望豌注。 院中可真熱鬧伤塌,春花似錦、人聲如沸轧铁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽齿风。三九已至药薯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間救斑,已是汗流浹背童本。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脸候,地道東北人穷娱。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像纪他,于是被迫代替她去往敵國(guó)和親鄙煤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容