




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、做linux下的網(wǎng)絡(luò)編程有一段時(shí)間了,中間遇到過很多問題,其中不少是因?yàn)樽约簩?duì)網(wǎng)絡(luò)編程和網(wǎng)絡(luò)協(xié)議的一些基本概念搞不清楚,趁著今天沒心情干活就把自己在網(wǎng)絡(luò)編程方面的理解和一些經(jīng)驗(yàn)總結(jié)一下,Request For Comments。在諸多的網(wǎng)絡(luò)協(xié)議中接觸的最多也最緊密的無疑是TCP和UDP,SCTP之前因?yàn)轫?xiàng)目原因也研究過,不過最終由于方案修改給拋棄了,TCP年代已經(jīng)很久遠(yuǎn),在網(wǎng)上的資料也非常多,而且我感覺它是一種非常復(fù)雜的協(xié)議,感覺要把編好基于TCP的程序光簡單地了解幾個(gè)socket API是不夠的,剛開始接觸網(wǎng)絡(luò)編程的時(shí)候自己確實(shí)也吃了不少苦頭,后來我還專門拿時(shí)間出來閱讀了一下RFC,再加上長
2、時(shí)間的實(shí)踐總算也對(duì)TCP有所了解,把自己的一些經(jīng)驗(yàn)和教訓(xùn)都總結(jié)一下。首先說一下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)圖,記不清是從哪里找來的,也不知道直接版權(quán)該給誰,但這張圖應(yīng)該最終是出自于UNP第一卷的,那copyright就是UNP了吧。1.TCP連接狀態(tài)1 / 11連接建立的幾個(gè)狀態(tài)沒什么可說的,TCP的三次握手眾所周知,更重要的是TCP連接中止的幾個(gè)狀態(tài),應(yīng)該可以說是連接中止需要四次握手吧。當(dāng)Client調(diào)用close函數(shù)主動(dòng)關(guān)閉socket時(shí),連接狀態(tài)被標(biāo)記為F
3、IN_WAIT_1,Server在收到FIN之后read函數(shù)會(huì)返回0,這里server知道Client已經(jīng)關(guān)閉連接,回復(fù)ACK,這里client連接狀態(tài)被標(biāo)記為FIN_WAIT_2,接下來Server調(diào)用close函數(shù)關(guān)閉連接,這時(shí)候Server向client發(fā)送FIN,Client收到之后將狀態(tài)標(biāo)記為TIME_WAIT,并回復(fù)ACK。TIME_WAIT這個(gè)狀態(tài)存在的意義在于Client回復(fù)的ACK未必會(huì)被Server收到,可能在傳輸過程中導(dǎo)致包的丟失,而這里Server未收到ACK之后會(huì)重新向Client發(fā)送FIN,如果client未將狀態(tài)標(biāo)記為TIME_WAIT而是直接標(biāo)記為CLOSED,
4、則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)記為LAST_ACK,表示Server正在等待Client發(fā)送的最后一個(gè)ACK,當(dāng)Server
5、收到最后一個(gè)ACK便會(huì)將連接標(biāo)記為CLOSED,這時(shí)連接結(jié)束。TIME_WAIT這個(gè)狀態(tài)和套接字的SO_REUSEADDR選項(xiàng)是有關(guān)系的,這個(gè)留做后面討論。2.TCP連接異常情況TCP連接異常分為很多種情況,無論是客戶端程序還是服務(wù)器端程序都需要考慮周全的。Server在連接的過程中程序崩潰或者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í)s
6、end方法會(huì)觸發(fā)SIGPIPE信號(hào),表示通信管道已斷開,在程序中如果對(duì)該信號(hào)不做處理則會(huì)導(dǎo)致程序的崩潰,一般在程序開始時(shí)會(huì)忽略此信號(hào),則在這種情況下send函數(shù)會(huì)返回-1,表示發(fā)送失敗,處理SIGPIPE的代碼如下:前幾天實(shí)驗(yàn)室這個(gè)破項(xiàng)目非要加上什么流媒體的功能,簡單起見使用了VLC來實(shí)現(xiàn),客戶端這邊就得需要把相關(guān)的播放界面整合到現(xiàn)有的界面里面來,之前的客戶端UI我都是用GTK實(shí)現(xiàn)的,沒辦法,GTK用得比較多,相對(duì)熟練一些就用GTK來做了,沒想到要把VLC整到GTK里面來那么麻煩,原生的libvlc是不支持GTK的,需要加一層libvlc-gtk,從網(wǎng)上好不容易下載到了libvlc-gtk的源
7、碼,從哪里下的也記不清了,反正就是零散地幾個(gè)文件,沒有README甚至連Makefile都沒有,沒辦法首先得先寫個(gè)Makefile把它編譯一下,libvlc-gtk一共有八個(gè)文件,Makefile如下:struct sigaction sa;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ò)直接異?;驍嚅_,這時(shí)候Server不可能再給Client發(fā)送FIN包,而Client調(diào)
8、用send函數(shù)后會(huì)導(dǎo)致數(shù)據(jù)包一直重傳直接超時(shí)后返回-1,而recv函數(shù)也會(huì)一直阻塞直接超時(shí)后返回-1。這種情況就很難判斷是Server端進(jìn)程關(guān)閉還是網(wǎng)絡(luò)異常,這種情況一般會(huì)用TCP的KEEP ALIVE機(jī)制,每隔一定的時(shí)間向?qū)Ψ桨l(fā)送一個(gè)只有一字節(jié)數(shù)據(jù)內(nèi)容的數(shù)據(jù)包,對(duì)端收到后會(huì)返回一個(gè)ACK,以此來確保連接正常,如果未收到ACK,會(huì)嘗試重傳,直到重試規(guī)定次數(shù)后可以將與對(duì)端的連接標(biāo)記為斷開,send和recv將會(huì)返回-1。KEEP ALIVE的使用方法如下:int tcp_keep_alive(int socketfd)int keepAlive = 1;int keepIdle = 10; /*
9、 開始發(fā)送KEEP ALIVE數(shù)據(jù)包之前經(jīng)歷的時(shí)間 */int keepInterval = 10; /* KEEP ALIVE數(shù)據(jù)包之前間隔的時(shí)間 */int keepCount = 10; /* 重試的最大次數(shù) */if(setsockopt(socketfd , SOL_SOCKET , SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive) = -1)debug_info(set SO_KEEPALIVE failedn);return -1;if(setsockopt(socketfd , SOL_TCP , TCP_KEEPIDLE,(vo
10、id *)&keepIdle,sizeof(keepIdle) = -1)debug_info(set TCP_KEEPIDEL failedn);return -1;if(setsockopt(socketfd , SOL_TCP , TCP_KEEPINTVL,(void *)&keepInterval,sizeof(keepInterval) = -1)debug_info(set TCP_KEEPINTVL failedn);return -1;if(setsockopt(socketfd , SOL_TCP , TCP_KEEPCNT,(void *)&keepCount,sizeo
11、f(keepCount) = -1)debug_info(set TCP_KEEPCNT failedn);return -1;return 1;上面這個(gè)函數(shù)只針對(duì)Linux,昨天有網(wǎng)友告知在Mac OS上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
12、-endian字節(jié)順序,字節(jié)順序轉(zhuǎn)換是必需的。寫了一個(gè)小程序來檢測(cè)字節(jié)順序,不知道對(duì)不對(duì),Request For Comment.#include int main(int argc, char *argv)short s = 0x0102;if(*(unsigned char*)&s) = 2)printf(little endiann);else if(*(unsigned char*)&s) = 1)printf(big endiann);elseprintf(unknown endiann);return 0;3.關(guān)于send和recv寫過socket程序的人肯定都會(huì)知道send和re
13、cv函數(shù)并不會(huì)總是返回要求發(fā)送或讀取的字節(jié)數(shù),如:int ret = recv(sk, buf, 2096, 0);這句話并不總是讀取到完整地2096個(gè)字節(jié),相反地,大多數(shù)情況下都不能將buf讀滿,recv只能返回當(dāng)前可以讀取到的字節(jié)數(shù),如果協(xié)議規(guī)定本次讀取肯定會(huì)讀取到N個(gè)字節(jié),那我一般的做法會(huì)寫一個(gè)這樣的函數(shù)來確保讀取到固定的字節(jié)數(shù):int buf_recv(int sock, void *buf, size_t len, int flags)int n, ret;if(len = 0) return 0;for(n=0;n!=len &(ret = recv(sock, buf+n, le
14、n-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ù)的話,原因很簡單,當(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ì)帶來額外的開銷,之前寫的一個(gè)代理服務(wù)器的程序數(shù)據(jù)傳輸速度一直很低,后來修改了recv和send的緩沖區(qū)大小后速率提高了近一倍。4.
15、關(guān)于非阻塞模式一般應(yīng)用的時(shí)候都是使用阻塞式IO,至少我在大多數(shù)情況下都用的阻塞式IO,非阻塞很少應(yīng)用,但存在便我價(jià)值,我用到的非阻塞IO的情況一般是用來進(jìn)行超時(shí)connect,首先將socket設(shè)為非阻塞模式,connect立即返回-1,此時(shí)已向?qū)Χ税l(fā)送FIN,而并未來得及收到任何ACK,于是直接返回-1,但并不代表連接失敗,errno會(huì)被置為EINPROGRESS ,表示連接正在進(jìn)行中,然后通過select來設(shè)置socket可寫的超時(shí)時(shí)間,如果規(guī)定時(shí)間內(nèi)可寫,且socket并無出錯(cuò),則表示連接成功,socket出錯(cuò)則表示連接失敗,或規(guī)定時(shí)間內(nèi)不可寫則表示連接超時(shí),簡單地寫了如下代碼:#inc
16、lude#include#include#include#include#include#include int main(int argc, char *argv)int sk;int flags;int err = 0;int ret;socklen_t len;struct sockaddr_in addr;fd_set fd_write;struct timeval tv;if( (sk = socket(AF_INET, SOCK_STREAM, 0) = -1 ) perror(socket);return 1;if( (flags = fcntl(sk, F_GETFL, 0)
17、= -1 )perror(fcntl GET flags failed);return 1;if(fcntl(sk, F_SETFL, flags | O_NONBLOCK) = -1) perror(fcntl SET flags failed);return 1;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, (struct sockaddr*)&a
18、ddr, sizeof(addr) = -1) if(errno != EINPROGRESS) perror(connect);return 1;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, &
19、err, &len) = 0) if(err = 0) printf(connect successn); return 0; else fprintf(stderr, connect:%sn, strerror(err); return 1; elsefprintf(stderr, getsockopt:%sn, strerror(err);return 1;elsefprintf(stderr, connect(FD_ISSET) failedn);return 1;else if(ret = 0) fprintf(stderr, connect timeoutn);return 1;else fprintf(std
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 【正版授權(quán)】 IEC 60335-2-11:2024 EXV-CMV EN Household and similar electrical appliances - Safety - Part 2-11: Particular requirements for tumble dryers
- 農(nóng)業(yè)生態(tài)修復(fù)與環(huán)境治理合作合同
- 外墻涂料買賣合同
- 生物技術(shù)基因工程知識(shí)題集及解析
- 化學(xué)工程與工藝專業(yè)基礎(chǔ)試題集
- 六一兒童節(jié)探訪活動(dòng)方案
- 六一公司團(tuán)建音樂活動(dòng)方案
- 六一商城活動(dòng)方案
- 六一孕婦店活動(dòng)方案
- 六一搶牛奶活動(dòng)方案
- 實(shí)驗(yàn)題(7大類42題)原卷版-2025年中考化學(xué)二輪復(fù)習(xí)熱點(diǎn)題型專項(xiàng)訓(xùn)練
- CJ/T 362-2011城鎮(zhèn)污水處理廠污泥處置林地用泥質(zhì)
- 2025安全宣傳咨詢?nèi)栈顒?dòng)知識(shí)手冊(cè)
- DB31/T 1249-2020醫(yī)療廢物衛(wèi)生管理規(guī)范
- 四川省宜賓市翠屏區(qū)2025屆數(shù)學(xué)七下期末綜合測(cè)試試題含解析
- 鄉(xiāng)鎮(zhèn)合法性審查工作報(bào)告
- 寧波大學(xué)《通信原理》期末考試試題
- 生命體征監(jiān)測(cè)技術(shù)操作考核評(píng)分標(biāo)準(zhǔn)
- 第三章混合策略納什均衡ppt課件
- 粉塵濃度和分散度測(cè)定
- 一年級(jí)成長檔案
評(píng)論
0/150
提交評(píng)論