linux Socket-應(yīng)用編程 5Socket Api編程進(jìn)價(jià)2服務(wù)器端多進(jìn)程并發(fā)子進(jìn)程退出問(wèn)題TCP回射客戶/服務(wù)器模型總結(jié)TCP是個(gè)流協(xié)議q TCP是基于字節(jié)流傳輸?shù)?,只維護(hù)發(fā)送出去多少,確認(rèn)了多少,沒(méi)有維護(hù)消息與消息之間的邊界,因而可能導(dǎo)致粘包問(wèn)題q 粘包問(wèn)題解決方法是在應(yīng)用層維護(hù)消息邊界僵進(jìn)程與SIGCHLD信號(hào)服務(wù)器端避免僵尸進(jìn)程的方法:1)通過(guò)忽略SIGCHLD信號(hào),解決僵尸進(jìn)程q signal(SIGCHLD, SIG_IGN)q2)通過(guò)wait方法,解決僵尸進(jìn)程q signal(SIGCHLD, handle_sigchld);q wait(NULL)3)通過(guò)waitpid方法,解決僵尸進(jìn)程q signal(SIGCHLD, handle_sigchld);q wait(-1, NULL, WNOHANG) q6Socket Api 與TCP/IP的11種狀態(tài) TCP/IP協(xié)議的11種狀態(tài) 理解0:什么是主動(dòng)套接字,什么是被動(dòng)套接字?理解1:為什么TCP/IP要三次握手,和四次斷開(kāi)?理解2:客戶端狀態(tài)向前推進(jìn)過(guò)程,服務(wù)器端狀態(tài)向前推進(jìn)過(guò)程理解3:執(zhí)行主動(dòng)關(guān)閉的那一端,進(jìn)入TIME_WAIT狀態(tài)理解4:TIME_WAIT 時(shí)間是多長(zhǎng)2MSL (2倍的最大生命期時(shí)間) 原因:(ACK y+1)如果發(fā)送失敗可以重發(fā)。
服務(wù)器端處于closed狀態(tài),不等于客戶端也處于closed狀態(tài)理解5:圖上幾種狀態(tài),還有一種CLOSING狀態(tài)兩端同時(shí)關(guān)閉 將產(chǎn)生closing狀態(tài),最后雙方都進(jìn)入TIME_WAIT狀態(tài)實(shí)驗(yàn):1) 關(guān)閉服務(wù)方子進(jìn)程,觀察TCP/IP狀態(tài)2) 關(guān)閉客戶端,觀察TCP/IP狀態(tài)SIGPIPE如果對(duì)方socket已關(guān)閉,對(duì)等方再發(fā)寫(xiě)數(shù)據(jù),則會(huì)產(chǎn)生SIGPIPE信號(hào)q SIGPIPE信號(hào)會(huì)讓進(jìn)程終止(man 7 signal,閱讀SIGPIPE默認(rèn)ACT)q 往一個(gè)已經(jīng)接收FIN的套接中寫(xiě)是允許的,接收到FIN僅僅代表對(duì)方不再發(fā)送數(shù)據(jù)q 在收到RST段之后,如果再調(diào)用write就會(huì)產(chǎn)生SIGPIPE信號(hào),對(duì)于這個(gè)信號(hào)的處理我們通常忽略即可 signal(SIGPIPE, SIG_IGN); 結(jié)論:對(duì)SIGPIPE處理方法:1)忽略該信號(hào)即可signal(SIGPIPE, SIG_IGN);2)捕捉改變默認(rèn)行為T(mén)CP/IP 的RST段重置1)服務(wù)器端啟動(dòng)、客戶端啟動(dòng)2)服務(wù)器端先kill與客戶端通訊的子進(jìn)程,服務(wù)器端會(huì)給客戶端發(fā)送FIN分節(jié)此時(shí):只代表服務(wù)器端不發(fā)送數(shù)據(jù)了,不能代表客戶端不能往套接字中寫(xiě)數(shù)據(jù)。
3)如果子進(jìn)程此時(shí)寫(xiě)數(shù)據(jù)給服務(wù)器端(解除屏幕阻塞,輸入字符aaaa), 將要導(dǎo)致TCP/IP協(xié)議重置,產(chǎn)生RST段;產(chǎn)生SIGPIPE信號(hào)4)所以,一般情況下,需要我們處理SIGPIPE信號(hào),忽略即可close與shutdown區(qū)別q close終止了數(shù)據(jù)傳送的兩個(gè)方向q shutdown可以有選擇的終止某個(gè)方向的數(shù)據(jù)傳送或者終止數(shù)據(jù)傳送的兩個(gè)方向q shutdown how=1就可以保證對(duì)等方接收到一個(gè)EOF字符,而不管其他進(jìn)程是否已經(jīng)打開(kāi)了套接字而close不能保證,直到套接字引用計(jì)數(shù)減為0時(shí)才發(fā)送也就是說(shuō)直到所有的進(jìn)程都關(guān)閉了套接字思考1客戶端向服務(wù)器發(fā)送:FIN(close) E D C B A,問(wèn):服務(wù)器還能收到數(shù)據(jù)嗎?服務(wù)器還可以向客戶端回報(bào)文嗎?客戶端想在關(guān)閉之后,仍然能接收到回射服務(wù)器應(yīng)答(shutdown)思考2父進(jìn)程中close(conn);會(huì)不會(huì)向客戶端發(fā)送FIN報(bào)文段那? 文件的引用計(jì)數(shù)-1,當(dāng)減少為0,才會(huì)發(fā)送引用計(jì)數(shù)思考3: 客戶端//shutdown(sock, SHUT_WR);只關(guān)閉了寫(xiě);7五種I/O模型 阻塞I/O說(shuō)明1:當(dāng)上層應(yīng)用app1調(diào)用recv系統(tǒng)調(diào)用時(shí),如果對(duì)等方?jīng)]有發(fā)送數(shù)據(jù)(緩沖區(qū)沒(méi)有數(shù)據(jù)),上層應(yīng)用app1將阻塞(默認(rèn)行為,被linux內(nèi)核阻塞);說(shuō)明2:當(dāng)對(duì)等方發(fā)送了數(shù)據(jù),linux內(nèi)核recv端緩沖區(qū),有數(shù)據(jù)后,內(nèi)核會(huì)把數(shù)據(jù)copy給用戶空間。
然后上層應(yīng)用app1解除阻塞,執(zhí)行下一步操作非阻塞I/O說(shuō)明1: 上層應(yīng)用程序app2將套接字設(shè)置成非阻塞模式說(shuō)明2: 上層應(yīng)用程序app2輪詢調(diào)用recv函數(shù),接受數(shù)據(jù)若緩沖區(qū)沒(méi)有數(shù)據(jù),上層程序app2不會(huì)阻塞,recv返回值為-1,錯(cuò)誤碼是EWOULDBLOCK說(shuō)明3:上層應(yīng)用程序不斷輪詢有沒(méi)有數(shù)據(jù)到來(lái)會(huì)造成上層應(yīng)用忙等待大量消耗CPU很少直接用應(yīng)用范圍小,一般和selectIO復(fù)用配合使用I/O復(fù)用 說(shuō)明1: 上層應(yīng)用程序app3調(diào)用select機(jī)制(該機(jī)制有l(wèi)inux內(nèi)核支持,避免了app3忙等待進(jìn)行輪詢文件描述符的狀態(tài)變化說(shuō)明2:當(dāng)select管理的文件描述符沒(méi)有數(shù)據(jù)(或者狀態(tài)沒(méi)有變化時(shí)),上層應(yīng)用程序app3也會(huì)阻塞說(shuō)明3:好處select機(jī)制可以管理多個(gè)文件描述符說(shuō)明4:select可以看成一個(gè)管理者,用select來(lái)管理多個(gè)IO 一旦檢測(cè)到的一個(gè)I/O或者多個(gè)IO,有我們感興事件,發(fā)生,select函數(shù)將返回,返回值為檢測(cè)到的事件個(gè)數(shù)進(jìn)而可以利用select相關(guān)api函數(shù),操作具體事件說(shuō)明5:select函數(shù)可以設(shè)置等待時(shí)間,避免了上層應(yīng)用程序app3,長(zhǎng)期僵死說(shuō)明6: 和阻塞IO模型相比,selectI/O復(fù)用模型相當(dāng)于提前阻塞了。
等到有數(shù)據(jù)到來(lái)時(shí),再調(diào)用recv就不會(huì)發(fā)生阻塞信號(hào)驅(qū)動(dòng)I/O說(shuō)明1: 上層應(yīng)用程序app4建立SIGIO信號(hào)處理程序當(dāng)緩沖區(qū)有數(shù)據(jù)到來(lái),內(nèi)核會(huì)發(fā)送信號(hào)告訴上層應(yīng)用程序app4說(shuō)明2:上層應(yīng)用程序app4接收到信號(hào)后,調(diào)用recv函數(shù),因緩沖區(qū)有數(shù)據(jù),recv函數(shù)一般不會(huì)阻塞說(shuō)明3:這種用于模型用的比較少,屬于典型的“拉模式”即:上層應(yīng)用app4,需要調(diào)用recv函數(shù)把數(shù)據(jù)拉進(jìn)來(lái)異步I/O 說(shuō)明1:上層應(yīng)用程序app5調(diào)用aio_read函數(shù),同時(shí)提交一個(gè)應(yīng)用層的緩沖區(qū)buf;調(diào)用完畢后,不會(huì)阻塞上層應(yīng)用程序app5可以繼續(xù)其他任務(wù)說(shuō)明2:當(dāng)tcpip協(xié)議緩沖區(qū)有數(shù)據(jù)時(shí),linux主動(dòng)的把內(nèi)核數(shù)據(jù)copy到用戶空間然后再給上層應(yīng)用app5發(fā)送信號(hào);告訴app5數(shù)據(jù)有了,趕快處理吧!說(shuō)明3:典型的“推模式”說(shuō)明4: 效率最高的一種形式,上層應(yīng)用程序app5有異步處理的能力(在linux內(nèi)核的支持下,言外之意:處理其他任務(wù)的同時(shí),也可支持IO通訊)異步I/O指的是什么?上層應(yīng)用程序app5,在也可以干別的活的時(shí),可以接收數(shù)據(jù)(接受異步通信事件異步命令來(lái)源)與信號(hào)驅(qū)動(dòng)IO模型,上層應(yīng)用程序app5不需要調(diào)用recv函數(shù)。
結(jié)論:IO復(fù)用和異步IO是重點(diǎn)8 I/O復(fù)用模型 select基本概念其他重要概念補(bǔ)充? 阻塞IO– 數(shù)據(jù)沒(méi)有準(zhǔn)備好, 讀操作就會(huì)阻塞– 數(shù)據(jù)不能立即被收時(shí), 寫(xiě)操作就會(huì)阻塞– 打開(kāi)文件時(shí)阻塞, 直到某些條件發(fā)生? 非阻塞IO– 立即返回, 并用錯(cuò)誤值來(lái)表示當(dāng)前的狀態(tài)? 指定非阻塞方式– 打開(kāi)時(shí)指定O_NONBLOCK 標(biāo)志– 使用fcntl 打開(kāi)或關(guān)閉非阻塞方式? 網(wǎng)絡(luò)編程時(shí), 使用非阻塞, 用輪詢方式發(fā)送? 使用多線程可以避免使用非阻塞IO, 但是同步開(kāi)銷較大多路IO? 當(dāng)程序需要同時(shí)從兩個(gè)輸入讀數(shù)據(jù)時(shí)? 使用多進(jìn)程/多線程, 同步復(fù)雜, 進(jìn)程線程開(kāi)銷? 使用非阻塞IO, 交替輪詢? 通過(guò)信號(hào)使用異步IO, 無(wú)法判斷哪個(gè)IO完成? 多路IO: 把關(guān)心的IO放入一個(gè)列表, 調(diào)用多路函數(shù)? 多路IO函數(shù)阻塞, 直到有一個(gè)IO數(shù)據(jù)準(zhǔn)備好后返回? 返回后告訴調(diào)用者哪個(gè)描述符準(zhǔn)備好了select()實(shí)現(xiàn)說(shuō)明? 調(diào)用select時(shí)通過(guò)參數(shù)告訴內(nèi)核用戶感興趣的IO描述符? 關(guān)心的IO狀態(tài): 輸入,輸出或錯(cuò)誤? 調(diào)用者等待時(shí)間? 返回之后內(nèi)核告訴調(diào)用者多個(gè)描述符準(zhǔn)備好了? 哪些描述符發(fā)生了變化? 調(diào)用返回后對(duì)準(zhǔn)備好的描述符調(diào)用讀寫(xiě)操作? 不關(guān)心的描述符集合傳NULLselect() /* According to POSIX 1003.1-2001 */#include
? 監(jiān)視r(shí)eadfds來(lái)查看是否read的時(shí)候會(huì)被堵塞,注意,即便到了end-of-file,fd也是可讀的? 監(jiān)視writefds看寫(xiě)的時(shí)候會(huì)不會(huì)被堵塞? 監(jiān)視exceptfd是否出現(xiàn)了異常主要用來(lái)讀取OOB數(shù)據(jù),異常并不是指出錯(cuò)? 注意當(dāng)一個(gè)套接口出錯(cuò)時(shí),它會(huì)變得既可讀又可寫(xiě)? 如果有了狀態(tài)改變,會(huì)將其他fd清零,只有那些發(fā)生改變了的fd保持置位,以用來(lái)指示set中的哪一個(gè)改變了狀態(tài)? 參數(shù)n是所有set里所有fd里,具有最大值的那個(gè)fd的值加1fd_set 四個(gè)宏用來(lái)對(duì)fd_set進(jìn)行操作: FD_CLR(int fd, fd_set *set); FD_ISSET(int fd, fd_set *set); FD_SET(int fd, fd_set *set); FD_ZERO(fd_set *set);? FD_ZERO用來(lái)清空set;? FD_SET和FD_CLR用來(lái)對(duì)某個(gè)set添加和刪除一個(gè)fd;? FD_ISSET用來(lái)指示一個(gè)fd是不是一個(gè)set的一部分他很有用,用來(lái)看select后哪一個(gè)fd可用了time_outn timeout是從調(diào)用開(kāi)始到select返回前,會(huì)經(jīng)歷的最大等待時(shí)間。
n 兩種特殊情況:如果為值為0,會(huì)立刻返回n 如果timeout是NULL,會(huì)阻塞式等待 struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */ };n 一些調(diào)用使用3個(gè)空的set, n為zero, 一個(gè)非空的timeout來(lái)達(dá)到較為精確的sleep.n Linux中, select函數(shù)改變了timeout值,用來(lái)指示還剩下的時(shí)間,但很多實(shí)現(xiàn)并不改timeoutn 為了較好的可移植性,timeout在循環(huán)中需要被重新賦初值? timeout== NULL– 無(wú)限等待– 被信號(hào)打斷時(shí)返回1, errno 設(shè)置成 EINTR? timeout->tv_sec == 0 && tvptr->tv_usec == 0– 不等待立即返。



![[精編]吳教人[]13號(hào)](/Images/s.gif)








