




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
/做linux下的網(wǎng)絡(luò)編程有一段時(shí)間了,中間遇到過(guò)很多問(wèn)題,其中不少是因?yàn)樽约簩?duì)網(wǎng)絡(luò)編程和網(wǎng)絡(luò)協(xié)議的一些基本概念搞不清楚,趁著今天沒(méi)心情干活就把自己在網(wǎng)絡(luò)編程方面的理解和一些經(jīng)驗(yàn)總結(jié)一下,RequestForComments。在諸多的網(wǎng)絡(luò)協(xié)議中接觸的最多也最緊密的無(wú)疑是TCP和UDP,SCTP之前因?yàn)轫?xiàng)目原因也研究過(guò),不過(guò)最終由于方案修改給拋棄了,TCP年代已經(jīng)很久遠(yuǎn),在網(wǎng)上的資料也非常多,而且我感覺(jué)它是一種非常復(fù)雜的協(xié)議,感覺(jué)要把編好基于TCP的程序光簡(jiǎn)單地了解幾個(gè)socketAPI是不夠的,剛開(kāi)始接觸網(wǎng)絡(luò)編程的時(shí)候自己確實(shí)也吃了不少苦頭,后來(lái)我還專門(mén)拿時(shí)間出來(lái)閱讀了一下RFC,再加上長(zhǎng)時(shí)間的實(shí)踐總算也對(duì)TCP有所了解,把自己的一些經(jīng)驗(yàn)和教訓(xùn)都總結(jié)一下。首先說(shuō)一下TCP的狀態(tài)轉(zhuǎn)移圖,這個(gè)應(yīng)該是很重要的,了解TCP運(yùn)行周期的各種狀態(tài)才能更好地運(yùn)用netstat之類的應(yīng)用程序去對(duì)程序進(jìn)行調(diào)試,我這里收藏了一張圖,是TCP的狀態(tài)圖,記不清是從哪里找來(lái)的,也不知道直接版權(quán)該給誰(shuí),但這張圖應(yīng)該最終是出自于UNP第一卷的,那copyright就是UNP了吧。1.TCP連接狀態(tài)連接建立的幾個(gè)狀態(tài)沒(méi)什么可說(shuō)的,TCP的三次握手眾所周知,更重要的是TCP連接中止的幾個(gè)狀態(tài),應(yīng)該可以說(shuō)是連接中止需要四次握手吧。當(dāng)Client調(diào)用close函數(shù)主動(dòng)關(guān)閉socket時(shí),連接狀態(tài)被標(biāo)記為FIN_WAIT_1,Server在收到FIN之后read函數(shù)會(huì)返回0,這里server知道Client已經(jīng)關(guān)閉連接,回復(fù)ACK,這里client連接狀態(tài)被標(biāo)記為FIN_WAIT_2,接下來(lái)Server調(diào)用close函數(shù)關(guān)閉連接,這時(shí)候Server向client發(fā)送FIN,Client收到之后將狀態(tài)標(biāo)記為T(mén)IME_WAIT,并回復(fù)ACK。TIME_WAIT這個(gè)狀態(tài)存在的意義在于Client回復(fù)的ACK未必會(huì)被Server收到,可能在傳輸過(guò)程中導(dǎo)致包的丟失,而這里Server未收到ACK之后會(huì)重新向Client發(fā)送FIN,如果client未將狀態(tài)標(biāo)記為T(mén)IME_WAIT而是直接標(biāo)記為CLOSED,則Server發(fā)送的FIN會(huì)直接收到RST,導(dǎo)致Server端的發(fā)送錯(cuò)誤,因此Client需要保證有一個(gè)TIME_WAIT狀態(tài),而這個(gè)狀態(tài)會(huì)持續(xù)兩位的MSL(最大段生命周期),從而保證Server成功發(fā)送FIN并發(fā)送ACK,為了保證兩個(gè)數(shù)據(jù)段傳輸?shù)淖畲髸r(shí)間,因此TIME_WAIT持續(xù)的時(shí)間為兩倍的MSL。Server在收到第一個(gè)FIN之后會(huì)將狀態(tài)標(biāo)記為CLOSE_WAIT,此時(shí)是client主動(dòng)關(guān)閉連接,這里Server也需要調(diào)用Close給Client發(fā)送FIN(如上所述),之后Server的狀態(tài)標(biāo)記為L(zhǎng)AST_ACK,表示Server正在等待Client發(fā)送的最后一個(gè)ACK,當(dāng)Server收到最后一個(gè)ACK便會(huì)將連接標(biāo)記為CLOSED,這時(shí)連接結(jié)束。TIME_WAIT這個(gè)狀態(tài)和套接字的SO_REUSEADDR選項(xiàng)是有關(guān)系的,這個(gè)留做后面討論。2.TCP連接異常情況TCP連接異常分為很多種情況,無(wú)論是客戶端程序還是服務(wù)器端程序都需要考慮周全的。Server在連接的過(guò)程中程序崩潰或者CTRL+C中止程序,或者kill接Server進(jìn)程。這時(shí)會(huì)導(dǎo)致Server立即發(fā)送一個(gè)FIN數(shù)據(jù)包給Client,Client如果此時(shí)正在調(diào)用recv函數(shù),則recv函數(shù)返回0,表示服務(wù)器已關(guān)閉連接,如果Client調(diào)用send函數(shù)繼續(xù)向Server發(fā)送數(shù)據(jù),Server在收到后會(huì)回復(fù)RST,而此時(shí)send方法會(huì)觸發(fā)SIGPIPE信號(hào),表示通信管道已斷開(kāi),在程序中如果對(duì)該信號(hào)不做處理則會(huì)導(dǎo)致程序的崩潰,一般在程序開(kāi)始時(shí)會(huì)忽略此信號(hào),則在這種情況下send函數(shù)會(huì)返回-1,表示發(fā)送失敗,處理SIGPIPE的代碼如下:前幾天實(shí)驗(yàn)室這個(gè)破項(xiàng)目非要加上什么流媒體的功能,簡(jiǎn)單起見(jiàn)使用了VLC來(lái)實(shí)現(xiàn),客戶端這邊就得需要把相關(guān)的播放界面整合到現(xiàn)有的界面里面來(lái),之前的客戶端UI我都是用GTK實(shí)現(xiàn)的,沒(méi)辦法,GTK用得比較多,相對(duì)熟練一些就用GTK來(lái)做了,沒(méi)想到要把VLC整到GTK里面來(lái)那么麻煩,原生的libvlc是不支持GTK的,需要加一層libvlc-gtk,從網(wǎng)上好不容易下載到了libvlc-gtk的源碼,從哪里下的也記不清了,反正就是零散地幾個(gè)文件,沒(méi)有README甚至連Makefile都沒(méi)有,沒(méi)辦法首先得先寫(xiě)個(gè)Makefile把它編譯一下,libvlc-gtk一共有八個(gè)文件,Makefile如下:structsigactionsa;sa.sa_handler=SIG_IGN;sigaction(SIGPIPE,&sa,0);另外在這種情況下select函數(shù)也會(huì)立即返回,socket描述符會(huì)被設(shè)置,而試圖從該socket中recv數(shù)據(jù),則會(huì)返回-1。另外一種情況是Server系統(tǒng)崩潰或者網(wǎng)絡(luò)直接異?;驍嚅_(kāi),這時(shí)候Server不可能再給Client發(fā)送FIN包,而Client調(diào)用send函數(shù)后會(huì)導(dǎo)致數(shù)據(jù)包一直重傳直接超時(shí)后返回-1,而recv函數(shù)也會(huì)一直阻塞直接超時(shí)后返回-1。這種情況就很難判斷是Server端進(jìn)程關(guān)閉還是網(wǎng)絡(luò)異常,這種情況一般會(huì)用TCP的KEEPALIVE機(jī)制,每隔一定的時(shí)間向?qū)Ψ桨l(fā)送一個(gè)只有一字節(jié)數(shù)據(jù)內(nèi)容的數(shù)據(jù)包,對(duì)端收到后會(huì)返回一個(gè)ACK,以此來(lái)確保連接正常,如果未收到ACK,會(huì)嘗試重傳,直到重試規(guī)定次數(shù)后可以將與對(duì)端的連接標(biāo)記為斷開(kāi),send和recv將會(huì)返回-1。KEEPALIVE的使用方法如下:inttcp_keep_alive(intsocketfd){ intkeepAlive=1; intkeepIdle=10;/*開(kāi)始發(fā)送KEEPALIVE數(shù)據(jù)包之前經(jīng)歷的時(shí)間*/ intkeepInterval=10;/*KEEPALIVE數(shù)據(jù)包之前間隔的時(shí)間*/ intkeepCount=10;/*重試的最大次數(shù)*/
if(setsockopt(socketfd,SOL_SOCKET,SO_KEEPALIVE ,(void*)&keepAlive,sizeof(keepAlive))==-1){ debug_info("setSO_KEEPALIVEfailed\n"); return-1; }
if(setsockopt(socketfd,SOL_TCP,TCP_KEEPIDLE ,(void*)&keepIdle,sizeof(keepIdle))==-1){ debug_info("setTCP_KEEPIDELfailed\n"); return-1; }
if(setsockopt(socketfd,SOL_TCP,TCP_KEEPINTVL ,(void*)&keepInterval,sizeof(keepInterval))==-1){ debug_info("setTCP_KEEPINTVLfailed\n"); return-1; }
if(setsockopt(socketfd,SOL_TCP,TCP_KEEPCNT ,(void*)&keepCount,sizeof(keepCount))==-1){ debug_info("setTCP_KEEPCNTfailed\n"); return-1; } return1;}上面這個(gè)函數(shù)只針對(duì)Linux,昨天有網(wǎng)友告知在MacOS上TCP_KEEPIDLE,TCP_KEEPINTVL,TCP_KEEPCNT這些宏將未定義。另外對(duì)于這些參數(shù)的設(shè)置也是需要注意的,很多系統(tǒng)中它們的設(shè)置并不是對(duì)單個(gè)socket描述符起作用的,而是該機(jī)器上的所有socket描述符起作用的,所以這個(gè)需要注意(這個(gè)是從UNP里面看到的)。3.關(guān)于字節(jié)順序
Linux的主機(jī)字節(jié)順序是采用little-endian字節(jié)順序,而網(wǎng)絡(luò)字節(jié)順序是采用big-endian字節(jié)順序,字節(jié)順序轉(zhuǎn)換是必需的。寫(xiě)了一個(gè)小程序來(lái)檢測(cè)字節(jié)順序,不知道對(duì)不對(duì),RequestForComment.#include
intmain(intargc,char**argv){ shorts=0x0102; if((*(unsignedchar*)&s)==2) printf("littleendian\n"); elseif((*(unsignedchar*)&s)==1) printf("bigendian\n"); else printf("unknownendian\n");
return0;}3.關(guān)于send和recv寫(xiě)過(guò)socket程序的人肯定都會(huì)知道send和recv函數(shù)并不會(huì)總是返回要求發(fā)送或讀取的字節(jié)數(shù),如:intret=recv(sk,buf,2096,0);這句話并不總是讀取到完整地2096個(gè)字節(jié),相反地,大多數(shù)情況下都不能將buf讀滿,recv只能返回當(dāng)前可以讀取到的字節(jié)數(shù),如果協(xié)議規(guī)定本次讀取肯定會(huì)讀取到N個(gè)字節(jié),那我一般的做法會(huì)寫(xiě)一個(gè)這樣的函數(shù)來(lái)確保讀取到固定的字節(jié)數(shù):intbuf_recv(intsock,void*buf,size_tlen,intflags){ intn,ret; if(len==0)return0; for(n=0;n!=len&&(ret=recv(sock,buf+n,len-n,flags))!=-1&&ret;n+=ret); return(n!=len)?-1:n;}關(guān)于這兩個(gè)函數(shù)還有很重要的一點(diǎn)是應(yīng)該盡可能大地一次發(fā)送或接收更多地?cái)?shù)據(jù),當(dāng)然前提是緩沖區(qū)中有這些數(shù)據(jù)的話,原因很簡(jiǎn)單,當(dāng)通信鏈路很好的時(shí)候數(shù)據(jù)可能會(huì)填滿系統(tǒng)緩沖區(qū),而recv便是從緩沖區(qū)中讀取數(shù)據(jù),這時(shí)候一次讀取更多地字節(jié)就意味著可以少調(diào)用幾次recv函數(shù),而這些函數(shù)通常都是調(diào)用了系統(tǒng)調(diào)用,需要進(jìn)行內(nèi)核態(tài)和用戶態(tài)上下文的切換,也就意味著多調(diào)用幾次recv會(huì)帶來(lái)額外的開(kāi)銷(xiāo),之前寫(xiě)的一個(gè)代理服務(wù)器的程序數(shù)據(jù)傳輸速度一直很低,后來(lái)修改了recv和send的緩沖區(qū)大小后速率提高了近一倍。4.關(guān)于非阻塞模式一般應(yīng)用的時(shí)候都是使用阻塞式IO,至少我在大多數(shù)情況下都用的阻塞式IO,非阻塞很少應(yīng)用,但存在便我價(jià)值,我用到的非阻塞IO的情況一般是用來(lái)進(jìn)行超時(shí)connect,首先將socket設(shè)為非阻塞模式,connect立即返回-1,此時(shí)已向?qū)Χ税l(fā)送FIN,而并未來(lái)得與收到任何ACK,于是直接返回-1,但并不代表連接失敗,errno會(huì)被置為EINPROGRESS,表示連接正在進(jìn)行中,然后通過(guò)select來(lái)設(shè)置socket可寫(xiě)的超時(shí)時(shí)間,如果規(guī)定時(shí)間內(nèi)可寫(xiě),且socket并無(wú)出錯(cuò),則表示連接成功,socket出錯(cuò)則表示連接失敗,或規(guī)定時(shí)間內(nèi)不可寫(xiě)則表示連接超時(shí),簡(jiǎn)單地寫(xiě)了如下代碼:#include#include#include#include#include#include#include
intmain(intargc,char*argv[]){ intsk; intflags; interr=0; intret; socklen_tlen; structsockaddr_inaddr; fd_setfd_write; structtimevaltv;
if((sk=socket(AF_INET,SOCK_STREAM,0))==-1){ perror("socket"); return1; }
if((flags=fcntl(sk,F_GETFL,0))==-1){ perror("fcntlGETflagsfailed"); return1; }
if(fcntl(sk,F_SETFL,flags|O_NONBLOCK)==-1){ perror("fcntlSETflagsfailed"); return1; }
memset(&addr,0,sizeof(addr)); addr.sin_family=AF_INET; addr.sin_addr.s_addr=inet_addr("59.64.129.169"); addr.sin_port=htons(808);
if(connect(sk,(structsockaddr*)&addr,sizeof(addr))==-1 ){ if(errno!=EINPROGRESS){ perror("connect"); return1; } FD_ZERO(&fd_write); FD_SET(sk,&fd_write); tv.tv_sec=5; tv.tv_usec=0;
ret=select(sk+1,(fd_set*)0,&fd_write,(fd_set*)0,&tv);
if(ret>0){
if(FD_ISSET(sk,&fd_write)){ len=sizeof(int);
if(getsockopt(sk,SOL_SOCKET,SO_ERROR,&err,&len)==0){ if(err==0){printf("connectsuccess\n");return0;} else{fprintf(stderr,"connect:%s\n",strerror(err));return1;} }else{ fprintf(stderr,"getsockopt:%s\n",strerror(err)); return1; } }else{ fprintf(stderr,"connect(FD_ISSET)failed\n"); return1; }
}elseif(ret==0){ fprintf(stderr,"connecttimeout\n"); return1; }e
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 臨時(shí)棧橋施工方案
- 內(nèi)衣合作協(xié)議書(shū)模板
- 惠州棄渣場(chǎng)綠化施工方案
- 新聞發(fā)言稿格式
- 工作心得分享發(fā)言稿
- 寫(xiě)一篇齊桓公的發(fā)言稿
- 履職盡責(zé)發(fā)言稿
- 運(yùn)輸行發(fā)言稿
- 述職報(bào)告-金融風(fēng)險(xiǎn)管理
- 敘事化歷史教學(xué)法講座
- Adobe-Illustrator-(Ai)基礎(chǔ)教程
- 沒(méi)頭腦和不高興-竇桂梅.精選優(yōu)秀PPT課件
- 鋼棧橋計(jì)算書(shū)(excel版)
- 租賃合同審批表
- 事業(yè)單位綜合基礎(chǔ)知識(shí)考試題庫(kù) 綜合基礎(chǔ)知識(shí)考試題庫(kù).doc
- 巖石堅(jiān)固性和穩(wěn)定性分級(jí)表
- 譯林初中英語(yǔ)教材目錄
- 律師事務(wù)所函[]第號(hào)
- 物業(yè)交付后工程維修工作機(jī)制
- 農(nóng)作物病蟲(chóng)害專業(yè)化統(tǒng)防統(tǒng)治管理辦法
- 新形勢(shì)下如何做一名合格的鄉(xiāng)鎮(zhèn)干部之我見(jiàn)
評(píng)論
0/150
提交評(píng)論