linux網(wǎng)絡(luò)編程手記_第1頁
linux網(wǎng)絡(luò)編程手記_第2頁
linux網(wǎng)絡(luò)編程手記_第3頁
linux網(wǎng)絡(luò)編程手記_第4頁
linux網(wǎng)絡(luò)編程手記_第5頁
已閱讀5頁,還剩5頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、做linux下的網(wǎng)絡(luò)編程有一段時間了,中間遇到過很多問題,其中不少是因為自己對網(wǎng)絡(luò)編程和網(wǎng)絡(luò)協(xié)議的一些基本概念搞不清楚,趁著今天沒心情干活就把自己在網(wǎng)絡(luò)編程方面的理解和一些經(jīng)驗總結(jié)一下,RequestForComments。在諸多的網(wǎng)絡(luò)協(xié)議中接觸的最多也最緊密的無疑是TCP和UDPSCTP之前因為項目原因也研究過,不過最終由于方案修改給拋棄了,TCP年代已經(jīng)很久遠(yuǎn),在網(wǎng)上的資料也非常多,而且我感覺它是一種非常復(fù)雜的協(xié)議,感覺要把編好基于TCP的程序光簡單地了解幾個socketAPI是不夠的,剛開始接觸網(wǎng)絡(luò)編程的時候自己確實也吃了不少苦頭,后來我還專門拿時間出來閱讀了一下RFC,再加上長時間的實

2、踐總算也對TCP有所了解,把自己的一些經(jīng)驗和教訓(xùn)都總結(jié)一下。首先說一下TCP的狀態(tài)轉(zhuǎn)移圖,這個應(yīng)該是很重要的,了解TC聯(lián)行周期的各種狀態(tài)才能更好地運用netstat之類的應(yīng)用程序去對程序進(jìn)行調(diào)試,我這里收藏了一張圖,是TCP的狀態(tài)圖,記不清是從哪里找來的,也不知道直接版權(quán)該給誰,但這張圖應(yīng)該最終是出自于UNP第一卷的,那copyright就是UNP了吧。連接狀態(tài)TCP狀態(tài)轉(zhuǎn)換圖電第盧-X、*人送m:1r他也ACK裝書笈送:<i>哲弋V與,剛忖開安/上助打JF於G5TAbushed/慰Xclose廣w)!數(shù)據(jù)隹輸狀態(tài)!1;Ml!”clca!發(fā)這1FIN;_1洞均丁一::*:);()有

3、MK虔”企!被動吳聞覽式:<k>TIMI-_WAIT)11Y&L晌連接建立的幾個狀態(tài)沒什么可說的,TCP的三次握手眾所周知,更重要的是TCP連接中止的幾個狀態(tài),應(yīng)該可以說是連接中止需要四次握手吧O當(dāng)Client調(diào)用close函數(shù)主動關(guān)閉socket時,連接狀態(tài)被標(biāo)記為FIN_WAIT_1,Server在收到FIN之后read函數(shù)會返回0,這里server知道Client已經(jīng)關(guān)閉連接,回復(fù)ACK這里client連接狀態(tài)被標(biāo)記為FIN_WAIT_2,接下來Server調(diào)用close函數(shù)關(guān)閉連接,這時候Server向client發(fā)送FIN,Client收到之后將狀態(tài)標(biāo)記為TIME

4、_WAIT并回復(fù)ACKTIME_WAIT這個狀態(tài)存在的意義在于Client回復(fù)的ACK未必會被Server收到,可能在傳輸過程中導(dǎo)致包的丟失,而這里Server未收到ACK后會重新向Client發(fā)送FIN,如果client未將狀態(tài)標(biāo)記為TIME_WAIT而是直接標(biāo)記為CLOSED則Server發(fā)送的FIN會直接收到RS導(dǎo)致Server端的發(fā)送錯誤,因此Client需要保證有一個TIME_WAIT狀態(tài),而這個狀態(tài)會持續(xù)兩位的MSL最大段生命周期),從而保證Server成功發(fā)送FIN并發(fā)送ACK,為了保證兩個數(shù)據(jù)段傳輸?shù)淖畲髸r間,因此TIME_WAI琳續(xù)的時間為兩倍的MSLServer在收到第一個

5、FIN之后會將狀態(tài)標(biāo)記為CLOSE_WAIT此時是client主動關(guān)閉連接,這里Server也需要調(diào)用Close給Client發(fā)送FIN(如上所述),之后Server的狀態(tài)標(biāo)記為LAST_ACK表示Server正在等待Client發(fā)送的最后一個ACK當(dāng)Server收到最后一個AC領(lǐng)會將連接標(biāo)記為CLOSED這時連接結(jié)束。TIME_WAIT這個狀態(tài)和套接字的SO_REUSEAD謝是有關(guān)系的,這個留做后面討論。連接異常情況TCP連接異常分為很多種情況,無論是客戶端程序還是服務(wù)器端程序都需要考慮周全的。Server在連接的過程中程序崩潰或者CTRL+什止程序,或者kill接Server進(jìn)程。這時會導(dǎo)

6、致Server立即發(fā)送一個FIN數(shù)據(jù)包給Client,Client如果此時正在調(diào)用recv函數(shù),則recv函數(shù)返回0,表示服務(wù)器已關(guān)閉連接,如果Client調(diào)用send函數(shù)繼續(xù)向Server發(fā)送數(shù)據(jù),Server在收到后會回復(fù)RST而此時send方法會觸發(fā)SIGPIPE信號,表示通信管道已斷開,在程序中如果對該信號不做處理則會導(dǎo)致程序的崩潰,一般在程序開始時會忽略此信號,則在這種情況下send函數(shù)會返回-1,表示發(fā)送失敗,處理SIGPIPE的代碼如下:前幾天實驗室這個破項目非要加上什么流媒體的功能,簡單起見使用了VLC來實現(xiàn),客戶端這邊就得需要把相關(guān)的播放界面整合到現(xiàn)有的界面里面來,之前的客戶

7、端UI我都是用GTK實現(xiàn)的,沒辦法,GT即得比較多,相對熟練一些就用GTKM故了,沒想到要把VLC整到GTK里面來那么麻煩,原生的libvlc是不支持GTK勺,需要加一層libvlc-gtk,從網(wǎng)上好不容易下載到了libvlc-gtk的源碼,從哪里下的也記不清了,反正就是零散地幾個文件,沒有README至連Makefile都沒有,沒辦法首先得先寫個Makefile把它編譯一下,libvlc-gtk一共有八個文件,Makefile如下:structsigactionsa;=SIG_IGN;sigaction(SIGPIPE,&sa,0);另外在這種情況下select函數(shù)也會立即返回,so

8、cket描述符會被設(shè)置,而試圖從該socket中recv數(shù)據(jù),則會返回-1。另外一種情況是Server系統(tǒng)崩潰或者網(wǎng)絡(luò)直接異?;驍嚅_,這時候Server不可能再給Client發(fā)送FIN包,而Client調(diào)用send函數(shù)后會導(dǎo)致數(shù)據(jù)包一直重傳直接超時后返回-1,而recv函數(shù)也會一直阻塞直接超時后返回-1o這種情況就很難判斷是Server端進(jìn)程關(guān)閉還是網(wǎng)絡(luò)異常,這種情況一般會用TCP的KEEPALIVE機(jī)制,每隔一定的時間向?qū)Ψ桨l(fā)送一個只有一字節(jié)數(shù)據(jù)內(nèi)容的數(shù)據(jù)包,對端收到后會返回一個ACK以此來確保連接正常,如果未收到ACK,會嘗試重傳,直到重試規(guī)定次數(shù)后可以將與對端的連接標(biāo)記為斷開,send和

9、recv將會返回-1。KEEPALIVE的使用方法如下:inttcp_keep_alive(intsocketfd)intkeepAlive=1;intkeepIdle=10;/*開始發(fā)送KEEPALIV吸據(jù)包之前經(jīng)歷的時間*/intkeepInterval=10;/*KEEPALIVE數(shù)據(jù)包之前間隔的時間*/intkeepCount=10;/*重試的最大次數(shù)*/if(setsockopt(socketfd,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive)=-1)debug_info("setSO_KEEP

10、ALIVEfailedn");return-1;if(setsockopt(socketfd,SOL_TCP,TCP_KEEPIDLE,(void*)&keepIdle,sizeof(keepIdle)=-1)debug_info("setTCP_KEEPIDELfailedn");return-1;if(setsockopt(socketfd,SOL_TCP,TCP_KEEPINTVL,(void*)&keepInterval,sizeof(keepInterval)=-1)debug_info("setTCP_KEEPINTVLfai

11、ledn");return-1;if(setsockopt(socketfd,SOL_TCP,TCP_KEEPCNT,(void*)&keepCount,sizeof(keepCount)=-1)debug_info("setTCP_KEEPCNTfailedn");return-1;return1;上面這個函數(shù)只針對Linux,昨天有網(wǎng)友告知在MacOS上TCP_KEEPIDLE,TCP_KEEPINTVL,TCP_KEEPCNT這些宏將未定義。另外對于這些參數(shù)的設(shè)置也是需要注意的,很多系統(tǒng)中它們的設(shè)置并不是對單個socket描述符起作用的,而是該機(jī)器上

12、的所有socket描述符起作用的,所以這個需要注意(這個是從UNP里面看到的)。3. 關(guān)于字節(jié)順序Linux的主機(jī)字節(jié)順序是采用little-endian字節(jié)順序,而網(wǎng)絡(luò)字節(jié)順序是采用big-endian字節(jié)順序,字節(jié)順序轉(zhuǎn)換是必需的。寫了一個小程序來檢測字節(jié)順序,不知道對不對,RequestForComment.#includeintmain(intargc,char*argv)shorts=0x0102;if(*(unsignedchar*)&s)=2)printf("littleendiann");elseif(*(unsignedchar*)&s)=

13、1)printf("bigendiann");elseprintf("unknownendiann");return0;4. 關(guān)于send和recv寫過socket程序的人肯定都會知道send和recv函數(shù)并不會總是返回要求發(fā)送或讀取的字節(jié)數(shù),如:intret=recv(sk,buf,2096,0);這句話并不總是讀取到完整地2096個字節(jié),相反地,大多數(shù)情況下都不能將buf讀滿,recv只能返回當(dāng)前可以讀取到的字節(jié)數(shù),如果協(xié)議規(guī)定本次讀取肯定會讀取到N個字節(jié),那我一般的做法會寫一個這樣的函數(shù)來確保讀取到固定的字節(jié)數(shù):intbuf_recv(intsoc

14、k,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)于這兩個函數(shù)還有很重要的一點是應(yīng)該盡可能大地一次發(fā)送或接收更多地數(shù)據(jù),當(dāng)然前提是緩沖區(qū)中有這些數(shù)據(jù)的話,原因很簡單,當(dāng)通信鏈路很好的時候數(shù)據(jù)可能會填滿系統(tǒng)緩沖區(qū),而recv便是從緩沖區(qū)中讀取數(shù)據(jù),這時候一次讀取更多地字節(jié)就意味著可以少調(diào)用幾次recv函數(shù),而這些函數(shù)通常都是調(diào)用了系

15、統(tǒng)調(diào)用,需要進(jìn)行內(nèi)核態(tài)和用戶態(tài)上下文的切換,也就意味著多調(diào)用幾次recv會帶來額外的開銷,之前寫的一個代理服務(wù)器的程序數(shù)據(jù)傳輸速度一直很低,后來修改了recv和send的緩沖區(qū)大小后速率提高了近一倍。5. 關(guān)于非阻塞模式一般應(yīng)用的時候都是使用阻塞式IO,至少我在大多數(shù)情況下都用的阻塞式IO,非阻塞很少應(yīng)用,但存在便我價值,我用到的非阻塞IO的情況一般是用來進(jìn)行超時connect,首先將socket設(shè)為非阻塞模式,connect立即返回-1,此時已向?qū)Χ税l(fā)送FIN,而并未來得及收到任何ACK于是直接返回-1,但并不代表連接失敗,errno會被置為EINPROGRES蕨示連接正在進(jìn)行中,然后通過s

16、elect來設(shè)置socket可寫的超時時間,如果規(guī)定時間內(nèi)可寫,且socket并無出錯,則表示連接成功,socket出錯則表示連接失敗,或規(guī)定時間內(nèi)不可寫則表示連接超時,簡單地寫了如下代碼:#include#include#include#include#include#include#includeint main (int argc , int int int int socklen struct fd_set structchar * argv )sk;flags;err= 0;rett lensockaddr_in addr fd_writetimeval tvif(sk=socket

17、(AF_INET,SOCK_STREA,M0)=-1)perror("socket");return1;if(flags=fcntl(sk,F_GETFL,0)=-1)perror("fcntlGETflagsfailed");return1;if(fcntl(sk,F_SETFL,flags|O_NONBLOC)K=-1)perror("fcntlSETflagsfailed");return1;memse(t&addr,0,sizeof(addr);=AF_INET;=inet_addr("");=ht

18、ons(808);if(connect(sk,(structsockaddr*)&addr,sizeof(addr)=-1)if(errno!=EINPROGRES)Sperror("connect");return1;FD_ZER(O&fd_write);FD_SE(Tsk,&fd_write);=5;=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(g

19、etsockopt(sk,SOL_SOCKE,TSO_ERRO,R&err,&len)=0)if(err=0)printf("connectsuccessn");return0;elsefprintf(stderr,"connect:%sn",strerror(err);return1;elsefprintf(stderr,"getsockopt:%sn",strerror(err);return1;elsefprintf(stderr,"connect(FD_ISSET)failedn");return1;elseif(ret=0)fprintf(stderr,"connecttimeoutn");return1;els

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論