新聞中心
隨著互聯(lián)網(wǎng)技術(shù)的不斷發(fā)展,網(wǎng)絡(luò)通信在人們的日常生活中扮演著至關(guān)重要的角色。在現(xiàn)代化的網(wǎng)絡(luò)通信中,長(zhǎng)連接一直是一種非常常見(jiàn)的通信方式。Linux作為一個(gè)開(kāi)源的操作系統(tǒng),其提供的長(zhǎng)連接也一直備受人們青睞。本文將詳細(xì)介紹Linux長(zhǎng)連接的概念、原理、優(yōu)點(diǎn)和應(yīng)用,并對(duì)幾個(gè)常見(jiàn)的長(zhǎng)連接實(shí)現(xiàn)進(jìn)行了詳細(xì)分析。

成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站、成都外貿(mào)網(wǎng)站建設(shè)公司服務(wù)團(tuán)隊(duì)是一支充滿著熱情的團(tuán)隊(duì),執(zhí)著、敏銳、追求更好,是創(chuàng)新互聯(lián)的標(biāo)準(zhǔn)與要求,同時(shí)竭誠(chéng)為客戶提供服務(wù)是我們的理念。創(chuàng)新互聯(lián)建站把每個(gè)網(wǎng)站當(dāng)做一個(gè)產(chǎn)品來(lái)開(kāi)發(fā),精雕細(xì)琢,追求一名工匠心中的細(xì)致,我們更用心!
一、Linux長(zhǎng)連接的概念
Linux長(zhǎng)連接是指在一個(gè)TCP連接上保持連續(xù)不斷的信息傳輸,直到通信結(jié)束或有特別的指示。相對(duì)于短連接,長(zhǎng)連接在進(jìn)行網(wǎng)絡(luò)通信時(shí),不需要頻繁地建立和關(guān)閉連接,從而節(jié)省了資源和時(shí)間。在Linux中,長(zhǎng)連接通常通過(guò)socket實(shí)現(xiàn),可以實(shí)現(xiàn)可靠的數(shù)據(jù)傳輸。
二、Linux長(zhǎng)連接的原理
長(zhǎng)連接的實(shí)現(xiàn)原理是在TCP連接應(yīng)用層與傳輸層之間增加一個(gè)心跳機(jī)制。當(dāng)有數(shù)據(jù)需要發(fā)送時(shí),先發(fā)送心跳包,告訴對(duì)方連接還在,然后再發(fā)送數(shù)據(jù)。如果在一段時(shí)間內(nèi)沒(méi)有數(shù)據(jù)發(fā)送,就會(huì)發(fā)送一個(gè)更長(zhǎng)時(shí)間的心跳包,保持連接的有效性。這樣就可以避免服務(wù)端在一些可能很短暫的空閑時(shí)間內(nèi)關(guān)閉連接,以避免資源浪費(fèi)。
三、Linux長(zhǎng)連接的優(yōu)點(diǎn)
1.穩(wěn)定性
長(zhǎng)連接提供了更穩(wěn)定的通信方式,不需要頻繁地建立和關(guān)閉連接。相對(duì)于短連接,長(zhǎng)連接更不容易出現(xiàn)網(wǎng)絡(luò)中斷和斷線等問(wèn)題。
2.效率
長(zhǎng)連接可以實(shí)現(xiàn)數(shù)據(jù)在TCP連接上連續(xù)不斷地傳輸,減少了因建立和關(guān)閉連接引起的成本,同時(shí)也減少了網(wǎng)絡(luò)帶寬的消耗。在高并發(fā)和高負(fù)載的情況下,使用長(zhǎng)連接可以提高通信效率。
3.靈活性
長(zhǎng)連接還可以通過(guò)控制心跳包的發(fā)送時(shí)間調(diào)整連接的靈活性。根據(jù)具體應(yīng)用場(chǎng)景,可以靈活地調(diào)整心跳包的發(fā)送間隔,以更好地適應(yīng)網(wǎng)絡(luò)環(huán)境。
四、Linux長(zhǎng)連接的應(yīng)用
1.聊天應(yīng)用
在聊天應(yīng)用中,長(zhǎng)連接可以實(shí)現(xiàn)即時(shí)通信的功能。當(dāng)用戶發(fā)送一條消息時(shí),可以通過(guò)長(zhǎng)連接讓服務(wù)器實(shí)時(shí)地將消息推送給對(duì)方。使用長(zhǎng)連接,可以避免用戶頻繁請(qǐng)求服務(wù)器,實(shí)現(xiàn)更優(yōu)秀的體驗(yàn)。
2.直播應(yīng)用
在直播應(yīng)用中,長(zhǎng)連接可以實(shí)現(xiàn)實(shí)時(shí)的直播彈幕和在線人數(shù)等功能。當(dāng)用戶觀看一場(chǎng)直播時(shí),可以通過(guò)長(zhǎng)連接實(shí)時(shí)地推送彈幕和當(dāng)前在線人數(shù)等信息。
3.物聯(lián)網(wǎng)應(yīng)用
在物聯(lián)網(wǎng)應(yīng)用中,長(zhǎng)連接可以實(shí)現(xiàn)設(shè)備之間的實(shí)時(shí)通信。當(dāng)一個(gè)設(shè)備的狀態(tài)發(fā)生變化時(shí),可以通過(guò)長(zhǎng)連接實(shí)時(shí)地將數(shù)據(jù)推送到監(jiān)控設(shè)備或其他相關(guān)設(shè)備。
五、常見(jiàn)的Linux長(zhǎng)連接實(shí)現(xiàn)
1. Nginx
Nginx是一個(gè)高性能的Web服務(wù)器,其也支持長(zhǎng)連接技術(shù)。通過(guò)Nginx提供的keepalive模塊,可以實(shí)現(xiàn)高效的長(zhǎng)連接應(yīng)用。
2. WebSocket
WebSocket是一種基于HTTP的協(xié)議,可以實(shí)現(xiàn)瀏覽器和服務(wù)器之間的雙向通信。通過(guò)WebSocket,可以輕松實(shí)現(xiàn)長(zhǎng)連接技術(shù),其使用長(zhǎng)連接進(jìn)行通信。
3. Redis
Redis是一個(gè)支持緩存、消息隊(duì)列、發(fā)布訂閱等功能的存儲(chǔ)系統(tǒng)。在Redis中,也可以實(shí)現(xiàn)長(zhǎng)連接技術(shù),并通過(guò)發(fā)布訂閱機(jī)制實(shí)現(xiàn)實(shí)時(shí)通信。
Linux長(zhǎng)連接是一種穩(wěn)定高效的網(wǎng)絡(luò)通信方式,其在現(xiàn)代網(wǎng)絡(luò)通信中扮演著非常重要的角色。通過(guò)充分理解Linux長(zhǎng)連接的概念、原理、優(yōu)點(diǎn)和應(yīng)用,可以實(shí)現(xiàn)更高效、靈活、穩(wěn)定的網(wǎng)絡(luò)通信。
成都網(wǎng)站建設(shè)公司-創(chuàng)新互聯(lián),建站經(jīng)驗(yàn)豐富以策略為先導(dǎo)10多年以來(lái)專注數(shù)字化網(wǎng)站建設(shè),提供企業(yè)網(wǎng)站建設(shè),高端網(wǎng)站設(shè)計(jì),響應(yīng)式網(wǎng)站制作,設(shè)計(jì)師量身打造品牌風(fēng)格,熱線:028-86922220基于事件驅(qū)動(dòng)的高性能開(kāi)源網(wǎng)絡(luò)庫(kù)libevent介紹及安裝
libevent是一個(gè)輕量級(jí)的基于事件驅(qū)動(dòng)的高性能的開(kāi)源網(wǎng)絡(luò)庫(kù),并且支持多個(gè)平臺(tái),對(duì)多個(gè)平臺(tái)的I/O復(fù)用技術(shù)進(jìn)行了封裝,當(dāng)我們編譯庫(kù)的代碼時(shí),編譯的腳本將會(huì)根據(jù)OS支持的處理事件機(jī)制,來(lái)編譯相應(yīng)的代碼,從而在libevent接口上保持一致。
在當(dāng)前的服務(wù)器上,面對(duì)的主要問(wèn)題就是要能處理大量的連接。而通過(guò)libevent這個(gè)網(wǎng)絡(luò)庫(kù),我們就可以調(diào)用它的API來(lái)很好的解決上面的問(wèn)題。首先,可以來(lái)回顧一下,對(duì)這個(gè)問(wèn)題的傳統(tǒng)解決方法。
問(wèn)題:
如何處理多個(gè)客戶端連接
解決方案1:
I/O復(fù)用技術(shù)
這幾種方式都是同步I/O,即當(dāng)讀寫事件就緒,他們自己需要負(fù)責(zé)進(jìn)行讀寫,這個(gè)讀寫過(guò)程是阻塞的,而異步I/O則不需要自己負(fù)責(zé)讀寫,只需要通知負(fù)責(zé)讀寫的程序就可以了。
解決方案2:
多線程技術(shù)或多進(jìn)程技術(shù)
多線程技術(shù)和多進(jìn)程技術(shù)也可以處理高并發(fā)的數(shù)據(jù)連接,因?yàn)樵诜?wù)器中可以產(chǎn)生大量的進(jìn)程和線程和處理我們需要監(jiān)視的連接。但是,這兩種方式也是有很大的局限性的,比如多進(jìn)程模型就不適合大量的短連接,因?yàn)檫M(jìn)程的產(chǎn)生和關(guān)閉需要消耗較大的系統(tǒng)性能,同樣,還要進(jìn)程進(jìn)程間的通信,在CPU性能不足的情況下不太適合。而多線程技術(shù)則不太適合處理長(zhǎng)連接,因?yàn)楫?dāng)我們建立一個(gè)進(jìn)程時(shí),linux中會(huì)消耗8G的??臻g,如果我們的每個(gè)連接都杵著不斷開(kāi),那么大量連接長(zhǎng)連接后,導(dǎo)致的結(jié)果就是內(nèi)存的大量消耗。
解決方案3:
常用的上述二者復(fù)合使用
上述的兩種方法各具有優(yōu)缺點(diǎn),因此,我們可以將上述的方法結(jié)合起來(lái),這也是目前使用較多的處理高并發(fā)的方法。多進(jìn)程+I/O復(fù)用或者多線程+I/O復(fù)用。而在具體的實(shí)現(xiàn)上,又可以分為很多的方式。比如多線程+I/O復(fù)用技術(shù),我們使春旁轎用使用一個(gè)主線程負(fù)責(zé)監(jiān)聽(tīng)一個(gè)端口和接受的描述符是否有讀寫事件產(chǎn)生,如果有,則將事件分發(fā)給其他的工作進(jìn)程去完啟吵成,這也是進(jìn)程池的理念。
在說(shuō)完上述的高并發(fā)的處理方法之后,我們可以來(lái)介紹一個(gè)libevent的主要特色了。
同樣,lievent也是采用的上述系統(tǒng)提供的select,poll和epoll方法來(lái)進(jìn)行I/O復(fù)用,但是針對(duì)于多個(gè)系統(tǒng)平臺(tái)上的不同的I/O復(fù)用實(shí)現(xiàn)方式,libevent進(jìn)行了重新的封裝,并提供了統(tǒng)一的API接口。libevent在實(shí)現(xiàn)上使用了事件驅(qū)動(dòng)這種機(jī)制,其本質(zhì)上是一種Reactor模式。
在Libevent中也是一樣,向Libevent框架注冊(cè)相應(yīng)的事件和回調(diào)函數(shù);當(dāng)這些事件發(fā)生時(shí),Libevent會(huì)調(diào)用這些回調(diào)函數(shù)處理相應(yīng)的事件。
lbevent的事件支持三種,分別是網(wǎng)絡(luò)IO、定時(shí)器和信號(hào)。定時(shí)器的數(shù)據(jù)結(jié)構(gòu)使用最小堆(Min Heap),以提高效率。網(wǎng)絡(luò)IO和信號(hào)的數(shù)據(jù)結(jié)構(gòu)采用了雙向鏈表(TAILQ)。
更多l(xiāng)inux內(nèi)核視頻教程文本資料免費(fèi)獲取后臺(tái)私信【內(nèi)核】。
libevent的安裝很簡(jiǎn)單,我是直接從github上clone下一個(gè)源碼,然后進(jìn)行編譯安裝的。
具體的命令是(假設(shè)扒肆你已經(jīng)安裝了git):
現(xiàn)在的libevent版本已經(jīng)到達(dá)libevent2了,其增加了多線程的支持,API函數(shù)也發(fā)生了一些微小的變化。
如果你想知道更多的API使用情況,請(qǐng)點(diǎn)擊這里。
下面,就基于libevent2編寫一個(gè)聊天室服務(wù)器。
設(shè)計(jì)思想:
首先創(chuàng)建一個(gè)套接字,進(jìn)而創(chuàng)建一個(gè)事件對(duì)此端口進(jìn)行監(jiān)聽(tīng),將所請(qǐng)求的用戶組成一個(gè)隊(duì)列,并監(jiān)聽(tīng)所有的用戶事件,當(dāng)某個(gè)用戶說(shuō)話了,產(chǎn)生了讀事件,就將該用戶的發(fā)言發(fā)送給隊(duì)列中的其他用戶。
程序分析
需要包含的libevent函數(shù)頭:
創(chuàng)建一個(gè)client結(jié)構(gòu)體,接受連接后存放數(shù)據(jù):
先來(lái)看下mian函數(shù)的處理:
首先,函數(shù)初始化了一個(gè)用戶隊(duì)列tailq,接著創(chuàng)建了一個(gè)socket套接字,并將套接字設(shè)定為非阻塞模式,接著對(duì)一個(gè)全局的evbase事件,注冊(cè)了事件,事件源是listen_fd,回調(diào)函數(shù)是on_accept,事件發(fā)生的情況是EV_READ,而且標(biāo)志EV_PESIST表明該事件一直存在,而后開(kāi)啟事件掃描循環(huán)event_base_dispatch(evbase)。
再看一下回調(diào)函數(shù)on_accpet實(shí)現(xiàn):
這個(gè)回調(diào)函數(shù)的作用很顯然,就是接受了一個(gè)客戶端的請(qǐng)求,并申請(qǐng)好了一個(gè)client信息,將需要的內(nèi)容填寫好,在填寫中需要注意的是,又向上述的事件集evbase中注冊(cè)了一個(gè)bufferevent事件client->buf_ev,并注冊(cè)了回調(diào)函數(shù)buffered_on_read,buffered_on_error,這三個(gè)函數(shù)分別是當(dāng)接受后的連接發(fā)生了讀或者錯(cuò)誤事件后的執(zhí)行函數(shù)。接著,將用戶的client結(jié)構(gòu)放入了用戶的隊(duì)列tailq中去。
用戶的buffer可讀后的執(zhí)行函數(shù):
執(zhí)行函數(shù)的作用很明顯,將libevent管理中的buffer數(shù)據(jù)讀取出,存入本地的data數(shù)組內(nèi),然后對(duì)隊(duì)列中的client進(jìn)行檢索,如果不是發(fā)數(shù)據(jù)的client,則將數(shù)據(jù)寫入該client的buffer中,發(fā)送給該用戶。這里注意的是需要反復(fù)讀取buffer中的數(shù)據(jù),防止一個(gè)讀取并沒(méi)有讀取干凈,直到讀取不到數(shù)據(jù)為止。
buffer出錯(cuò)處理函數(shù)和上述函數(shù)差不多,功能就是出錯(cuò)后,結(jié)束掉保存的client結(jié)構(gòu),詳細(xì)就不說(shuō)了。
編譯的時(shí)候記得修改Makefile中Libevent文件夾的位置
設(shè)計(jì)思想:
所謂回顯服務(wù)器就是將客戶端發(fā)過(guò)來(lái)的數(shù)據(jù)再發(fā)回去,這里主要也就是說(shuō)明libevent的純IO復(fù)用實(shí)現(xiàn)。實(shí)現(xiàn)方法和上面的差不多,甚至可以說(shuō)更加簡(jiǎn)單。
程序和上面的聊天服務(wù)器差不多,只是在buffer可讀的事件函數(shù)中,不是將用戶的數(shù)據(jù)發(fā)送給其他用戶,而是直接發(fā)送給用戶本身。
設(shè)計(jì)思想:
上面的方法單純使用libevent的簡(jiǎn)單函數(shù)來(lái)實(shí)現(xiàn)服務(wù),但是這里,我們假設(shè)我們需要處理的客戶端很少,于是我們可以使用對(duì)于每個(gè)連接我們分配一個(gè)線程這樣的方式來(lái)實(shí)現(xiàn)對(duì)用戶的服務(wù)。這種方式簡(jiǎn)單有效,一對(duì)一服務(wù),就算業(yè)務(wù)邏輯出現(xiàn)阻塞也不怕。
程序分析
首先定義了一些數(shù)據(jù)結(jié)構(gòu),worker數(shù)據(jù)結(jié)構(gòu)定義的是一個(gè)工作者,它包含有一個(gè)工作線程,和結(jié)束標(biāo)志,需要獲取的工作隊(duì)列,和建立鏈表需要的指針。job數(shù)據(jù)結(jié)構(gòu)定義的是操作一個(gè)job的方法和對(duì)象,這回到程序中,實(shí)際上就是指的是事件發(fā)生后,封裝好的client結(jié)構(gòu)體和處理這個(gè)結(jié)構(gòu)體的方法。workqueue數(shù)據(jù)結(jié)構(gòu)指的是當(dāng)前的工作隊(duì)列中的工作者,以及工作隊(duì)列中的待完成的工作,以及互斥鎖和條件變量(因?yàn)槎鄠€(gè)工作進(jìn)程需要訪問(wèn)這些資源)。
具體的流程就是,用一個(gè)主線程監(jiān)聽(tīng)一個(gè)套接字,并將套接字接受到的連接accept,并創(chuàng)建一個(gè)client數(shù)據(jù)結(jié)構(gòu)保存該連接的信息,在這個(gè)client結(jié)構(gòu)中注冊(cè)一個(gè)bufferevent事件,注冊(cè)到client->evbase上(這時(shí)候這是向client中的evbase注冊(cè)了一個(gè)事件還沒(méi)有進(jìn)行循環(huán)這個(gè)事件集)。
接著,當(dāng)監(jiān)聽(tīng)到某個(gè)client有bufferevent事件發(fā)生,主線程就把該client結(jié)構(gòu)體和需要進(jìn)行的工作方法包裝成一個(gè)job結(jié)構(gòu),然后把這個(gè)job扔到workqueue上去,并通知各個(gè)工作者。而后,各個(gè)工作者開(kāi)著的線程就被激活了,瘋狂地去workqueue上去搶工作做,某個(gè)worker拿到工作后,就可以解包job,根據(jù)job的工作說(shuō)明書(job_function)操作工作對(duì)象(client)了。這里,job的工作說(shuō)明有是循環(huán)client中的client->evbase,于是這樣線程就會(huì)一直去監(jiān)視這個(gè)連接的狀態(tài),如果有數(shù)據(jù)就這會(huì)調(diào)用回調(diào)函數(shù)進(jìn)行處理。同時(shí),這個(gè)線程也就是阻塞在這里,這對(duì)這一個(gè)連接負(fù)責(zé)。
建立workqueue需要的結(jié)構(gòu)體和函數(shù)有:
主線程的on_accept函數(shù)為:
job中的工作指南為:
設(shè)計(jì)思想:
假設(shè)我們的用戶很多,高并發(fā),長(zhǎng)連接,那么我們還是來(lái)用I/O復(fù)用和線程池實(shí)現(xiàn)吧,用一個(gè)控制線程通過(guò)I/O復(fù)用負(fù)責(zé)監(jiān)聽(tīng)和分發(fā)事件,用一組線程池來(lái)進(jìn)行處理事件,這樣就可以靈活地將控制邏輯和業(yè)務(wù)邏輯分開(kāi)了,見(jiàn)下述講解。
程序分析
具體的流程和上面的差不多,用一個(gè)主線程監(jiān)聽(tīng)一個(gè)套接字,并將套接字接受到的連接accept,并創(chuàng)建一個(gè)client數(shù)據(jù)結(jié)構(gòu)保存該連接的信息,在這個(gè)client結(jié)構(gòu)中注冊(cè)一個(gè)bufferevent事件,但是這里,將事件注冊(cè)到accept_evbase中,仍然用主線程進(jìn)行監(jiān)聽(tīng)。
而面對(duì)監(jiān)聽(tīng)后出現(xiàn)的事件,將client和操作client的方法打包成一個(gè)job,放到上述的workqueue中去,讓工作進(jìn)程來(lái)完成。這樣的操作和上述的差別在于上述方法將bufferevent注冊(cè)到client中的evbase中,用工作線程監(jiān)聽(tīng),而本方法用主線程監(jiān)聽(tīng),工作線程負(fù)責(zé)處理監(jiān)聽(tīng)產(chǎn)生的事件。
這要的差別在于兩個(gè)函數(shù) on_accept函數(shù):
在buffered_on_read中,提交job。
在job工作指南server_job_function中就可以做你工作該做的事兒了,根據(jù)發(fā)來(lái)的信息進(jìn)行數(shù)據(jù)庫(kù)處理,http返回等等。
暢談linux下TCP(上)
tcp 協(xié)議 是互聯(lián)網(wǎng)中最常用的協(xié)議 , 開(kāi)發(fā)人員基本上天天和它打交道,對(duì)它進(jìn)行深入了解。 可以幫助我們排查定位bug和進(jìn)行程序優(yōu)化。下面我將就TCP幾個(gè)點(diǎn)做深入的探討
客戶端:收到 ack 后 分配連接資源。 發(fā)送數(shù)據(jù)
服務(wù)器 : 收到 syn 后立即 分配連接資源
客戶端:收到ACK, 立即分配資源
服務(wù)器:收到ACK, 立即分配資源
既然三次握手也液銀顫不是100%可靠, 那四次,五次,六次。。。呢? 其實(shí)都一樣,不管多少次都有丟包問(wèn)題。
client 只發(fā)送一個(gè) SYN, server 分配一個(gè)tcb, 放入syn隊(duì)列中。 這時(shí)候連接叫
半連接
狀態(tài);如果server 收不到 client 的ACK, 會(huì)不停重試 發(fā)送 ACK-SYN 給client 。重試間隔 為 2 的 N 次方 疊加(2^0 , 2^1, 2^2 ….);直至超時(shí)才釋放syn隊(duì)列中的這個(gè) TCB;
在半連接狀態(tài)下, 一方面會(huì)占用隊(duì)列配額資源,另一方面占用內(nèi)存資源。我們應(yīng)該讓半連接狀態(tài)存在時(shí)間盡可能的小
當(dāng)client 向一個(gè)未打開(kāi)的端口發(fā)起連接請(qǐng)求時(shí),會(huì)收到一個(gè)RST回復(fù)包
當(dāng)listen 的 backlog 和 somaxconn 都設(shè)置了得時(shí)候, 取兩者min值
Recv-Q 是accept 隊(duì)列當(dāng)前個(gè)數(shù), Send-Q 設(shè)置更大值
這種SYN洪水攻擊是一種常見(jiàn)攻擊方式,就是利用半連接隊(duì)列特性,占滿syn 隊(duì)列的 資源,導(dǎo)致 client無(wú)法連接上。
解決方案:
為什么不像握手那樣合并成三次揮手? 因?yàn)楹蛣傞_(kāi)始連接情況,連接是大家都從0開(kāi)始, 關(guān)閉時(shí)有歷史包袱的。server(被動(dòng)關(guān)閉方) 收到 client(主動(dòng)關(guān)閉方) 的關(guān)閉請(qǐng)求FIN包。 這時(shí)候可能還有未發(fā)送完的數(shù)據(jù),不能丟棄。 所以需要分開(kāi)。事實(shí)可能是這樣
當(dāng)然,在沒(méi)有待發(fā)數(shù)據(jù),并且允許 Delay ACK 情況下, FIN-ACK合并還是非常常見(jiàn)的事情,這是三次揮手是可以的。
同上
CLOSE_WAIT 是被動(dòng)關(guān)閉方才有的狀態(tài)
。
被動(dòng)關(guān)閉方 到 期間的狀態(tài)為 CLOSE_WAIT, 這個(gè)狀態(tài)仍然能發(fā)鬧敗送數(shù)據(jù)。 我們叫做
半關(guān)閉
, 下面用個(gè)例子來(lái)分析:
這個(gè)是我實(shí)際生產(chǎn)環(huán)境碰到的一個(gè)問(wèn)題,長(zhǎng)連接會(huì)話場(chǎng)景,server端收到client的rpc call 請(qǐng)求1,處理發(fā)現(xiàn)請(qǐng)求包有問(wèn)題,就強(qiáng)制關(guān)閉結(jié)束這次會(huì)話, 但是 因?yàn)閏lient 發(fā)送 第二次請(qǐng)求之前,并沒(méi)有去調(diào)用recv,所以并不知道 這個(gè)連接被server關(guān)閉, 繼續(xù)發(fā)送 請(qǐng)求2 , 此時(shí)是半連接,能夠成功發(fā)送到對(duì)端機(jī)器,但是recv結(jié)果后,搏兆遇到連接已經(jīng)關(guān)閉錯(cuò)誤。
如果 client 和 server 恰好同時(shí)發(fā)起關(guān)閉連接。這種情況下,兩邊都是主動(dòng)連接,都會(huì)進(jìn)入 TIME_WAIT狀態(tài)
1、
被動(dòng)關(guān)閉方在LAST_ACK狀態(tài)(已經(jīng)發(fā)送FIN),等待主動(dòng)關(guān)閉方的ACK應(yīng)答,但是 ACK丟掉, 主動(dòng)方并不知道,以為成功關(guān)閉。因?yàn)闆](méi)有TIME_WAIT等待時(shí)間,可以立即創(chuàng)建新的連接, 新的連接發(fā)送SYN到前面那個(gè)未關(guān)閉的被動(dòng)方,被動(dòng)方認(rèn)為是收到錯(cuò)誤指令,會(huì)發(fā)送RST。導(dǎo)致創(chuàng)建連接失敗。
2、
主動(dòng)關(guān)閉方斷開(kāi)連接,如果沒(méi)有TIME_WAIT等待時(shí)間,可以馬上建立一個(gè)新的連接,但是前一個(gè)已經(jīng)斷開(kāi)連接的,延遲到達(dá)的數(shù)據(jù)包。 被新建的連接接收,如果剛好seq 和 ack字段 都正確, seq在滑動(dòng)窗口范圍內(nèi)(只能說(shuō)機(jī)率非常小,但是還是有可能會(huì)發(fā)生),會(huì)被當(dāng)成正確數(shù)據(jù)包接收,導(dǎo)致數(shù)據(jù)串包。 如果不在window范圍內(nèi),則沒(méi)有影響( 發(fā)送一個(gè)確認(rèn)報(bào)文(ack 字段為期望ack的序列號(hào),seq為當(dāng)前發(fā)送序列號(hào)),狀態(tài)變保持原樣)
TIME_WAIT 問(wèn)題比較比較常見(jiàn),特別是CGI機(jī)器,并發(fā)量高,大量連接后段服務(wù)的tcp短連接。因此也衍生出了多種手段解決。雖然每種方法解決不是那么完美,但是帶來(lái)的好處一般多于壞處。還是在日常工作中會(huì)使用。
1、改短TIME_WAIT 等待時(shí)間
這個(gè)是之一個(gè)想到的解決辦法,既然等待時(shí)間太長(zhǎng),就改成時(shí)間短,快速回收端口。但是實(shí)際情況往往不樂(lè)觀,對(duì)于并發(fā)的機(jī)器,你改多短才能保證回收速度呢,有時(shí)候幾秒鐘就幾萬(wàn)個(gè)連接。太短的話,就會(huì)有前面兩種問(wèn)題小概率發(fā)生。
2、禁止Socket lingering
這種情況下關(guān)閉連接,會(huì)直接拋棄緩沖區(qū)中待發(fā)送的數(shù)據(jù),會(huì)發(fā)送一個(gè)RST給對(duì)端,相當(dāng)于直接拋棄TIME_WAIT, 進(jìn)入CLOSE狀態(tài)。同樣因?yàn)槿∠?TIME_WAIT 狀態(tài),會(huì)有前面兩種問(wèn)題小概率發(fā)生。
3、tcp_tw_reuse
net.ipv4.tcp_tw_reuse選項(xiàng)是 從 TIME_WAIT 狀態(tài)的隊(duì)列中,選取條件:1、remote 的 ip 和端口相同, 2、選取一個(gè)時(shí)間戳小于當(dāng)前時(shí)間戳; 用來(lái)解決端口不足的尷尬。
現(xiàn)在端口可以復(fù)用了,看看如何面對(duì)前面TIME_WAIT 那兩種問(wèn)題。 我們仔細(xì)回顧用一下前面兩種問(wèn)題。
都是在新建連接中收到老連接的包導(dǎo)致的問(wèn)題
, 那么如果我能在新連接中識(shí)別出此包為非法包,是不是就可以丟掉這些無(wú)用包,解決問(wèn)題呢。
需要實(shí)現(xiàn)這些功能,需要擴(kuò)展一下tcp 包頭。 增加 時(shí)間戳字段。 發(fā)送者 在每次發(fā)送的時(shí)候。 在tcp包頭里面帶上發(fā)送時(shí)候的時(shí)間戳。 當(dāng)接收者接收的時(shí)候,在ACK應(yīng)答中除了TCP包頭中帶自己此時(shí)發(fā)送的時(shí)間戳,并且把收到的時(shí)間戳附加在后面。也就是說(shuō)ACK包中有兩個(gè)時(shí)間戳字段。結(jié)構(gòu)如下:
那我們接下來(lái)一個(gè)個(gè)分析tcp_tw_reuse是如何解決TIME_WAIT的兩個(gè)問(wèn)題的
4、tcp_tw_recycle
tcp_tw_recycle 也是借助 timestamp機(jī)制。顧名思義, tcp_tw_reuse 是復(fù)用 端口,并不會(huì)減少 TIME-WAIT 數(shù)量。你去查詢機(jī)器上TIME-WAIT 數(shù)量,還是 幾千幾萬(wàn)個(gè),這點(diǎn)對(duì)有強(qiáng)迫癥的同學(xué)感覺(jué)很不舒服。tcp_tw_recycle 是 提前 回收 TIME-WAIT資源。會(huì)減少 機(jī)器上 TIME-WAIT 數(shù)量。
關(guān)于linux 長(zhǎng)連接的介紹到此就結(jié)束了,不知道你從中找到你需要的信息了嗎 ?如果你還想了解更多這方面的信息,記得收藏關(guān)注本站。
創(chuàng)新互聯(lián)【028-86922220】值得信賴的成都網(wǎng)站建設(shè)公司。多年持續(xù)為眾多企業(yè)提供成都網(wǎng)站建設(shè),成都品牌建站設(shè)計(jì),成都高端網(wǎng)站制作開(kāi)發(fā),SEO優(yōu)化排名推廣服務(wù),全網(wǎng)營(yíng)銷讓企業(yè)網(wǎng)站產(chǎn)生價(jià)值。
文章名稱:Linux長(zhǎng)連接:穩(wěn)定高效的網(wǎng)絡(luò)通信方式(linux長(zhǎng)連接)
地址分享:http://www.5511xx.com/article/ccsspeo.html


咨詢
建站咨詢
