讀寫socket套接字的正確姿勢(shì)
掃描二維碼
隨時(shí)隨地手機(jī)看文章
導(dǎo)學(xué)問(wèn)題:
1. 增加緩沖區(qū),可以提高程序的吞吐量嗎?
2. 網(wǎng)絡(luò)數(shù)據(jù)從發(fā)送到接受的過(guò)程,總共拷貝了多少次?
發(fā)送數(shù)據(jù)
ssize_t write (int socketfd, const void *buffer, size_t size)
ssize_t send (int socketfd, const void *buffer, size_t size, int flags)
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)
這三個(gè)都是發(fā)送程序時(shí)常用的接口,大部分人可以都很熟悉write函數(shù)了,經(jīng)典的寫文件接口。我們這里重點(diǎn)給大家介紹后兩種。
如果我們想發(fā)送緊急數(shù)據(jù),那么我們就可以使用send接口,給發(fā)送的數(shù)據(jù)加上緊急標(biāo)記,那么我們的這一幀數(shù)據(jù)就會(huì)快速被發(fā)送出去。
如果想指定多重緩沖區(qū)進(jìn)行數(shù)據(jù)傳輸,就需要使用第三個(gè)函數(shù),以結(jié)構(gòu)體msghdr的方式發(fā)送數(shù)據(jù)。
發(fā)送緩沖區(qū)
當(dāng)tcp連接成功后,在linux內(nèi)核里面會(huì)維護(hù)一個(gè)發(fā)送緩存區(qū)。它的大小可以通過(guò)調(diào)整套接字選項(xiàng)來(lái)進(jìn)行設(shè)置。當(dāng)我們應(yīng)用程序調(diào)用write函數(shù)時(shí),實(shí)際上是把數(shù)據(jù)從應(yīng)用程序拷貝到操作系統(tǒng)內(nèi)核的發(fā)送緩存區(qū),它并不一定馬上就會(huì)發(fā)送出去。具體情況如下:
- 發(fā)送緩存區(qū)足夠大,那么write的系統(tǒng)調(diào)用會(huì)馬上退出,返回寫入的字節(jié)數(shù)就是應(yīng)用程序的大小。
- 發(fā)送緩存區(qū)不足以容納應(yīng)用程序的數(shù)據(jù),那么write函數(shù)會(huì)一直阻塞,直到應(yīng)用數(shù)據(jù)完全填充到緩存區(qū)后返回,緩存區(qū)的數(shù)據(jù)由操作系統(tǒng)內(nèi)核負(fù)責(zé)把它發(fā)送出去。
如下圖:

所以無(wú)限增大緩沖區(qū)肯定不行,內(nèi)核緩沖區(qū)總是充滿數(shù)據(jù)時(shí)會(huì)產(chǎn)生粘包問(wèn)題,同時(shí)網(wǎng)絡(luò) 的傳輸大小MTU也會(huì)限制每次發(fā)送的大小,最后由于數(shù)據(jù)堵塞需要消耗大量?jī)?nèi)存資源,資 源使用效率不高。相當(dāng)于讓倉(cāng)庫(kù)變大,可以存儲(chǔ)了更多的貨物,如果出貨的速度有限,會(huì)有更多的貨物爛在倉(cāng)庫(kù)里。
讀取數(shù)據(jù)
最簡(jiǎn)單的數(shù)據(jù)讀取方式就是read函數(shù)了,它的函數(shù)原型如下:
ssize_t read (int socketfd, void *buffer, size_t size)
read函數(shù)要求操作系統(tǒng)內(nèi)核從套接字描述子socketfd讀取固定數(shù)量的字節(jié),并將讀取的結(jié)果存儲(chǔ)到buffer中。返回值告訴我們讀取的實(shí)際字節(jié)數(shù)量。特殊情況下,如果它的返回值為0,那么代表EOF,表示網(wǎng)絡(luò)中對(duì)端發(fā)送了FIN,我們也要相應(yīng)地進(jìn)行斷開連接處理。而如果返回-1,則代表出錯(cuò)。
阻塞式套接字最終發(fā)送返回的實(shí)際寫入字節(jié)數(shù)和請(qǐng)求字節(jié)數(shù)是相等的。發(fā)送成功僅僅表示的是數(shù)據(jù)被拷貝到了發(fā)送緩沖區(qū)中,并不意味著連接對(duì)端已經(jīng)收到所有的數(shù)據(jù)。至于什么時(shí)候發(fā)送到對(duì)端的接收緩沖區(qū),或者更進(jìn)一步說(shuō),什么時(shí)候被對(duì)方應(yīng)用程序緩沖所接收,對(duì)我們而言完全都是透明的。
總結(jié)
對(duì)于send發(fā)送數(shù)據(jù)來(lái)說(shuō),返回成功僅代表數(shù)據(jù)寫到發(fā)送緩存區(qū)成功,并不代表對(duì)端已經(jīng)接收成功。而對(duì)于read來(lái)說(shuō),需要循壞讀取數(shù)據(jù),并且要考慮EOF等異常條件。