UNIX的網(wǎng)絡(luò)通信初步_第1頁
UNIX的網(wǎng)絡(luò)通信初步_第2頁
UNIX的網(wǎng)絡(luò)通信初步_第3頁
UNIX的網(wǎng)絡(luò)通信初步_第4頁
UNIX的網(wǎng)絡(luò)通信初步_第5頁
已閱讀5頁,還剩102頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

第六章UNIX的網(wǎng)絡(luò)通信初步,(1)UNIX操作系統(tǒng)為進(jìn)程通信提供了相應(yīng)設(shè)施,如管道(pipe)、命名管道(namedpipe)和軟中斷信號(hào)(signal),消息(message)、共享存儲(chǔ)區(qū)(sharedmemory)和信號(hào)量(semaphore)等,但這些都只限于用在本機(jī)進(jìn)程間的通信。(2)為了實(shí)現(xiàn)計(jì)算機(jī)全面聯(lián)網(wǎng)與信息的異地處理,需要為用戶構(gòu)建Client/Server應(yīng)用的通訊結(jié)構(gòu),通過網(wǎng)絡(luò)接口編程,以解決不同主機(jī)進(jìn)程間的通信問題。,6.1網(wǎng)絡(luò)接口,在UNIX系統(tǒng)中,網(wǎng)絡(luò)接口有兩類:一類是源自BSDUNIX的Sockets(套接口),另一類是UNIXSystemV的TLI(TransmissionLayerInterface)。TLI是根據(jù)工業(yè)標(biāo)準(zhǔn)“ISO傳輸服務(wù)定義(ISO8072)”實(shí)現(xiàn)的,由于SVR3只包括了流以及TLI構(gòu)建模塊而并沒有任何的如TCP/IP之類的協(xié)議,因此TLI具有與協(xié)議無關(guān)性,關(guān)鍵技術(shù)是定義了一組對許多傳輸協(xié)議公共的服務(wù)。目前TLI的修正版XTL在UNIX系統(tǒng)中仍然得到廣泛的使用。SocketAPI是基于各種傳輸協(xié)議之上的,目前已經(jīng)成為網(wǎng)絡(luò)編程的既成事實(shí)標(biāo)準(zhǔn)?;赟ocketsAPI的通用性,本章只討論SocketsAPI的應(yīng)用。,目前最通用的提供遠(yuǎn)程進(jìn)程間通信的API是伯克利套接字(Berkeleysocket)接口。所謂的套接字是一種抽象數(shù)據(jù)結(jié)構(gòu),用以創(chuàng)建一條在沒有相關(guān)聯(lián)的進(jìn)程間發(fā)送、接收消息的通道(連接點(diǎn))。這些進(jìn)程在通信前各自建立一個(gè)Socket,并通過對Socket的讀寫操作實(shí)現(xiàn)通信功能。當(dāng)使用基于套接字的連接時(shí),服務(wù)器端進(jìn)程創(chuàng)建一個(gè)套接字,并把它映射到一個(gè)本地地址上,然后等待(監(jiān)聽)客戶端的請求??蛻舳诉M(jìn)程也創(chuàng)建自己的套接字,并確定服務(wù)器端的具體位置(比如主機(jī)名,端口號(hào)等)。依靠傳輸連接方式的應(yīng)答,客戶端進(jìn)程就可以開始發(fā)送和接收數(shù)據(jù),而不用管是否接收到服務(wù)器進(jìn)程的正式確認(rèn)(應(yīng)答)。,每個(gè)套接字都有其類型和一個(gè)與之相連的進(jìn)程。當(dāng)應(yīng)用程序創(chuàng)建套接字時(shí),套接字系統(tǒng)調(diào)用返回句柄,即所謂套接字描述字,它和文件描述字是有所區(qū)別的:當(dāng)文件描述字,由open命令創(chuàng)建時(shí),它被耦合到特定的文件或設(shè)備;當(dāng)套接字描述字由Socket命令創(chuàng)建時(shí),它并不被耦合到任何位置。當(dāng)套接字用作面向連接的網(wǎng)絡(luò)傳輸接口時(shí),應(yīng)用程序可用bind命令將套接字明確地耦合到一個(gè)地址。當(dāng)套接字用作無連接的網(wǎng)絡(luò)傳輸接口時(shí),應(yīng)用程序可以在用sendto命令發(fā)送數(shù)據(jù)報(bào)時(shí)動(dòng)態(tài)地提供地址。,6.1.1套接口的類型,UNIX提供下列四種類型的socket:數(shù)據(jù)流套接字(SOCK_STREAM),它提供雙向的、面向連接的、可靠的、有序的并且不重復(fù)的無記錄邊界數(shù)據(jù)流。一對相連的流Socket提供幾乎類似于管道的接口。流式socket針對于TCP服務(wù)應(yīng)用,如文件傳送協(xié)議(FTP)。數(shù)據(jù)流套接字采用TCP協(xié)議,這是個(gè)有連接的協(xié)議,在數(shù)據(jù)正式傳輸前必須建立連接,此連接是個(gè)穩(wěn)定的雙向線路,可以保證提供無錯(cuò)誤的傳送管道,因?yàn)橹灰獍趥魉瓦^程發(fā)生錯(cuò)誤損毀、次序錯(cuò)亂或送錯(cuò),TCP將會(huì)察覺問題并要求重新發(fā)送數(shù)據(jù),因此適合在需要大量的數(shù)據(jù)傳輸并要求完全正確的狀況時(shí)使用。,6.1.1套接口的類型,數(shù)據(jù)報(bào)套接字(SOCK_DGRAM),它也支持雙向數(shù)據(jù)流,但數(shù)據(jù)以獨(dú)立包形式被發(fā)送,無可靠性保證、無序、數(shù)據(jù)可能丟失或重復(fù)。數(shù)據(jù)報(bào)socket提供一個(gè)無連接服務(wù),對應(yīng)于無連接的UDP服務(wù)應(yīng)用,如網(wǎng)絡(luò)文件系統(tǒng)(NFS)、組播通信。數(shù)據(jù)報(bào)套接字采用UDP協(xié)議,這是個(gè)無連接的協(xié)議,發(fā)送主機(jī)直接將封包送至目的主機(jī),無需事先建立連接。因?yàn)楸苊饬私⑦B接所需的高代價(jià),采用數(shù)據(jù)報(bào)方式效率較高,但數(shù)據(jù)報(bào)方式自身不能處理數(shù)據(jù)傳輸過程出現(xiàn)的錯(cuò)誤,因此使用數(shù)據(jù)報(bào)方式的應(yīng)用程序必須自己處理這些問題。一般在比較簡單的網(wǎng)絡(luò)應(yīng)用程序中使用數(shù)據(jù)報(bào)方式。,6.1.1套接口的類型,原始套接字(SOCK_RAW),它提供對支持socket概念的基本通信協(xié)議的訪問。該接口允許用戶訪問支持套接字抽象的底層通信協(xié)議,如IP、ICMP直接訪問,常用于檢驗(yàn)新的協(xié)議實(shí)現(xiàn)或訪問現(xiàn)有服務(wù)中配置的新設(shè)備。順序報(bào)套接字:這種類型的套接字類似數(shù)據(jù)流套接字,不同的是其傳送的數(shù)據(jù)具有記錄邊界。,6.1.2套接口支持的協(xié)議,1套接字協(xié)議簇(family)(1)AF-UNIX:UNIXDomain協(xié)議,在該域中創(chuàng)建的套接字只能為同在一個(gè)主機(jī)的進(jìn)程所用;(2)AF-INET:Internet協(xié)議,這個(gè)域里的套接字允許在不同主機(jī)上的不相關(guān)的進(jìn)程間進(jìn)行通訊;2套接字協(xié)議(Protocol)(1)TCP協(xié)議(傳輸控制協(xié)議):負(fù)責(zé)保證兩臺(tái)主機(jī)之間傳輸?shù)姆纸M到達(dá)目的地,保證分組以正確的順序(確切的說,分組按正確的順序重新編排)并無差錯(cuò)地到達(dá)目的地。當(dāng)分組在兩臺(tái)主機(jī)間的路徑上丟失時(shí),TCP協(xié)議確保重傳丟失的分組;(2)UDP協(xié)議(用戶數(shù)據(jù)報(bào)協(xié)議):類似TCP協(xié)議,但是它是不可靠的。UDP不對分組進(jìn)行檢查、重新排序和重傳;,6.1.2套接口支持的協(xié)議,6.1.3套接口地址結(jié)構(gòu),大多數(shù)套接口函數(shù)都需要一個(gè)指向套接口地址結(jié)構(gòu)的指針作參數(shù),而在實(shí)際應(yīng)用中各協(xié)議的地址結(jié)構(gòu)是不同的,例如IPv4是32位的,而IPv6則是128的。由于早期定義的原因,UNIX的系統(tǒng)函數(shù)都只支持通用的地址結(jié)構(gòu)而無法區(qū)分特定協(xié)議的地址結(jié)構(gòu),因此在調(diào)用這些函數(shù)時(shí)必須將指向具體協(xié)議的套接口地址結(jié)構(gòu)的指針類型轉(zhuǎn)換成指向通用套接口地址結(jié)構(gòu)的類型。如serv是IPv4的地址格式,可以通過(structsockaddr*)/*地址總長度*/u_shortsa_family;/*協(xié)議族,AF_xxx*/charsa_data14;/*具體協(xié)議地址*/;其中:sa_family為套接字協(xié)議簇類型;sa_data中存儲(chǔ)具體的協(xié)議地址,不同的協(xié)議簇有不同的地址結(jié)構(gòu),如TCP/IP協(xié)議的套接字地址結(jié)構(gòu)是在文件中定義的sockaddr_in結(jié)構(gòu),這個(gè)結(jié)構(gòu)定義如下:,IPv4套接口地址結(jié)構(gòu):(在頭文件中定義)structsockaddru_charsin_len;/*32位的IPv4地址總長度*/u_shortsin_family;/*協(xié)議族,AF_INET*/u_shortsin_port;/*協(xié)議端口號(hào)*/charsin_zero8;/*保留,置0*/;要注意的是,套接口的地址結(jié)構(gòu)是按網(wǎng)絡(luò)字節(jié)順序而不是按主機(jī)存儲(chǔ)字節(jié)順序來存儲(chǔ)的(網(wǎng)絡(luò)字節(jié)是從高到低的順序,而主機(jī)是從低到高的順序),因此,在Internet上傳輸數(shù)據(jù)時(shí)就需要進(jìn)行轉(zhuǎn)換,否則就會(huì)出現(xiàn)數(shù)據(jù)不一致。下面是幾個(gè)常用的字節(jié)順序轉(zhuǎn)換函數(shù):htonl():把32位值從主機(jī)字節(jié)序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序ntohl():把32位值從網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換成主機(jī)字節(jié)序,通用套接口地址結(jié)構(gòu)IPv4套接口地址結(jié)構(gòu),長度AF_INIET16位端口號(hào)sin_portsin_zero8,長度AF_INIET16位端口號(hào)sin_portsa_data16,6.2網(wǎng)絡(luò)通信的系統(tǒng)函數(shù),6.2.1建立網(wǎng)絡(luò)連接的數(shù)據(jù)結(jié)構(gòu)一個(gè)用戶為了執(zhí)行網(wǎng)絡(luò)I/O操作,它首先要調(diào)用函數(shù)socket()。這個(gè)函數(shù)陷入內(nèi)核后執(zhí)行sys_socket()系統(tǒng)調(diào)用,后者為用戶創(chuàng)建一個(gè)名為socket的數(shù)據(jù)結(jié)構(gòu),并對其進(jìn)行一些簡單的初始化工作,然后返回一個(gè)小的正整數(shù),代表這個(gè)socket結(jié)構(gòu)。這個(gè)小的正整數(shù)與文件描述符功能類似,所以把它稱作套接字描述符。,6.2.1建立網(wǎng)絡(luò)連接的數(shù)據(jù)結(jié)構(gòu),structsocketsocket_statestate;unsignedlongflags;structproto_ops*ops;structinode*inode;structfasync_struct*fasync_list;structfile*file;structspck*sk;wait_queue_head_twait;shorttype;unsignedcharpasscred;unsignedchartli;其中:state描述該套接字的狀態(tài)信息;flags表示連接時(shí)的一些控制信息;ops指向oroto_ops結(jié)構(gòu)類型的指針;inode是指向inode結(jié)構(gòu)類型的指針;fasync_list存在異步進(jìn)行處理的不同文件的指針;sk指向socket結(jié)構(gòu)的指針;wait表示等待在該socket結(jié)構(gòu)上的人物列表;type表示數(shù)據(jù)包的類型。,6.2.2網(wǎng)絡(luò)連接的建立和關(guān)閉,1建立一個(gè)套接字描述符函數(shù)格式如下:intsocket(intfamily,inttype,intprotocol);socket()調(diào)用中有三個(gè)參數(shù),第一個(gè)參數(shù)family是一個(gè)整數(shù)型的量,指定協(xié)議簇;第二個(gè)參數(shù)是type,表示套接字的類型;第三個(gè)參數(shù)protocol用來表示在指定協(xié)議族中使用哪種特定協(xié)議,大多情況下,該參數(shù)被設(shè)為0,讓系統(tǒng)自己去選擇基于協(xié)議族的協(xié)議。Socket()調(diào)用成功則返回一個(gè)正整數(shù),即套接字描述符,用以標(biāo)識(shí)該套接字;如果調(diào)用失敗,會(huì)返回1,并設(shè)置全局變量errno為相應(yīng)的錯(cuò)誤類型。,Sockek()函數(shù)的執(zhí)行流程,Socket(),Sys_socketcall(),Sys_socket(),Socket_create(),Socket_alloc(),Inet_creat(),Sk_alloc(),Sock_alloc()用來創(chuàng)建一個(gè)socket結(jié)構(gòu),Sk_alloc()用來創(chuàng)建一個(gè)sock結(jié)構(gòu),系統(tǒng)調(diào)用接口,Sock_create(.)sock_alloc();inet_creat();,例1:創(chuàng)建一個(gè)套接字對#include#include#include#include#includemain(void)intsock2,cpid,i;/*套接字對*/staticcharbufBUF_SZ;/*消息的臨時(shí)緩沖區(qū)*/if(socketpair(PF_UNIX,SOCK_TREAM,0,sock)0)perror(“Generationerror”);exit(1);switch(cpid=(int)fork()case-1:perror(“Backfork”);exit(2);case0:/*子進(jìn)程*/close(sock1);for(i=0;i10;i+=2)sleep(1);sprintf(buf,“c:%dn”,i);write(sock0,buf,sizeof(buf);read(sock0,buf,BUF_SZ);printf(“c%s”,buf);close(sock0);break;,default:/*父進(jìn)程*/close(sock0);for(i=0;i10;i+=2)sleep(1);read(sock1,buf,BUF_SZ);printf(“p%s”,buf);sprintf(buf,“p:%dn”,i);write(sock1,buf,sizeof(buf);close(sock1);return0;,6.2.2網(wǎng)絡(luò)連接的建立和關(guān)閉,2指定本機(jī)地址及端口bind()intbind(intsockfd,structsockaddr*my_addr,intaddrlen);/*0成功;-1出錯(cuò)*/其中:參數(shù)sockfd是調(diào)用socket()返回的套接字,參數(shù)my_addr是通用地址結(jié)構(gòu)指針,參數(shù)addrlen是該結(jié)構(gòu)的長度,常被設(shè)置為sizeof(structsockaddr)。該函數(shù)為套接字分配一個(gè)本地協(xié)議地址,對于IP協(xié)議來說是IP地址和TCP或UDP端口號(hào)的組合。bind()調(diào)用在UNIX域中用來聯(lián)系套接字和它的名字(一個(gè)文件名),在因特網(wǎng)域用來將本地地址和套接字綁定在一起,包括IP地址和端口號(hào)。它是依據(jù)第二個(gè)參數(shù)的值的不同而不同的;套接字和本地地址的綁定采用組合的方式,如下表,注:INADDR-ANY在UNIX系統(tǒng)中被映射為0的常量,6.2.2網(wǎng)絡(luò)連接的建立與關(guān)閉,使用bind函數(shù)時(shí),可以用下面的賦值實(shí)現(xiàn)自動(dòng)獲得本機(jī)IP地址和隨機(jī)獲取一個(gè)沒有被占用的端口號(hào):my_addr.sin_port=0;/*系統(tǒng)隨機(jī)選擇一個(gè)未被使用的端口號(hào)*/my_addr.sin_addr.s_addr=INADDR_ANY;/*填入本機(jī)IP地址*/,6.2.2網(wǎng)絡(luò)連接的建立與關(guān)閉,3客戶啟動(dòng)連接connect()intconnect(intsockfd,structsockaddr*serv_addr,intaddrlen);/*返回:0成功;-1出錯(cuò)*/sockfd是調(diào)用socket()返回的套接字;serv_addr是包含遠(yuǎn)端服務(wù)器IP地址和端口號(hào)的通用地址結(jié)構(gòu)的指針;addrlen是遠(yuǎn)端地址結(jié)構(gòu)的長度。TCP客戶用Connect函數(shù)建立一個(gè)與遠(yuǎn)端TCP服務(wù)器的連接,正是connect激發(fā)了TCP三次握手的連接過程。由于協(xié)議族總被包含在套接字地址結(jié)構(gòu)的前兩個(gè)字節(jié)中,并通過socket()調(diào)用與某個(gè)協(xié)議族相關(guān)。因此bind()和connect()無須協(xié)議作為參數(shù)。,connect(),用戶空間,核心空間,sys_socketcall(),sys_connect(),inet_stream_coonect(),tcp_v4_connect(),ip_route_output(),ip_route_output_key(),ip_route_connect(),ip_output(),ip_route_optput_slow(),系統(tǒng)調(diào)用,傳輸層(TCP協(xié)議),網(wǎng)絡(luò)層(IPv4或IPv6),6.2.2網(wǎng)絡(luò)連接的建立與關(guān)閉,4監(jiān)聽連接listen()intlisten(intsockfd,intbacklog);/*返回:0成功;-1出錯(cuò)*/sockfd是調(diào)用socket()建立的套接字,它是一個(gè)socket調(diào)用成功后的返回值;backlog是指定在請求隊(duì)列中允許的最大請求數(shù),一般大于5的均設(shè)為5。進(jìn)入的連接請求將在隊(duì)列中等待accept()調(diào)用建立與客戶的連接。該函數(shù)僅被TCP服務(wù)器調(diào)用,它總是使套接口處于被動(dòng)的監(jiān)聽模式,并為該套接口建立一個(gè)輸入數(shù)據(jù)隊(duì)列,將到達(dá)的服務(wù)請求保存在此隊(duì)列中,直到程序處理它們。如果一個(gè)服務(wù)請求到來時(shí),輸入隊(duì)列已滿,該套接口將拒絕連接請求并顯示出錯(cuò)信息。listen()調(diào)用將一個(gè)尚未連接的主動(dòng)套接字轉(zhuǎn)換成為一個(gè)被動(dòng)套接字,使其可以接收連接請求,因?yàn)橛蓅ocket()調(diào)用所創(chuàng)建的套接字(主動(dòng)套接字)只可以用來進(jìn)行主動(dòng)連接,不能接收連接的請求。,5服務(wù)器接受連接accept()intaccept(intsockfd,structsockaddr*addr,int*addrlen);/*返回:0成功;-1出錯(cuò)*/accept()調(diào)用從傾聽套接字的連接隊(duì)列中接收第一個(gè)連接,生成一個(gè)新的套接字來完成客戶機(jī)的要求,原來的套接字繼續(xù)監(jiān)視網(wǎng)絡(luò),等待用戶的連接。accept()調(diào)用的第一個(gè)參數(shù)sockfd是用來標(biāo)識(shí)從哪個(gè)套接字中接收連接的,addr是一個(gè)指向客戶方套接字地址結(jié)構(gòu)的變量指針,該變量用來接收提出連接請求服務(wù)的客戶的協(xié)議地址,指明某臺(tái)主機(jī)從某個(gè)端口發(fā)出該請求;addr的確切格式由套接字創(chuàng)建時(shí)建立的地址族決定。addrten通常為一個(gè)指向值為sizeof(structsockaddr)的整型指針變量,指明客戶方套接字地址結(jié)構(gòu)的長度(字節(jié)數(shù))。accept()只用于TCP服務(wù)器,且在調(diào)用前應(yīng)該先調(diào)用過listen()。當(dāng)listen()偵聽到有連接請求到達(dá)時(shí),accept()調(diào)用將請求連接隊(duì)列上的第一個(gè)客戶的套接字地址及長度放入addr和addrlen,并與該客戶在sockfd建立連接。,6.2.2網(wǎng)絡(luò)連接的建立與關(guān)閉,6關(guān)閉套接字close()close(intsockfd);參數(shù)sockfd是待關(guān)閉的套接字。close()是標(biāo)準(zhǔn)的關(guān)閉函數(shù),在TCP服務(wù)中激發(fā)該套接字的連接關(guān)閉。函數(shù)功能只是對該套接字作“關(guān)閉標(biāo)識(shí)”表明不可用,而連接的另一方還在試圖發(fā)送排隊(duì)的數(shù)據(jù)。只有當(dāng)對方發(fā)現(xiàn)通信的套接字已不可用,自己也調(diào)用close()關(guān)閉本機(jī)的套接字,才真正地結(jié)束數(shù)據(jù)的發(fā)送。,6.2.3發(fā)送數(shù)據(jù),UNIX內(nèi)核為用戶提供的發(fā)送數(shù)據(jù)的系統(tǒng)調(diào)用有5個(gè),它們分別是:1)write():與文件系統(tǒng)中的write()完全一致;2)writev():與write功能相似,所不同的是writev可以在一次函數(shù)調(diào)用中寫多個(gè)緩沖區(qū)(集中寫)3)send():面向連接的發(fā)送數(shù)據(jù)過程(TCP);4)sendto():面向無連接的發(fā)送數(shù)據(jù)過程(UDP)5)sendmsg():直接使用msghdr結(jié)構(gòu)發(fā)送數(shù)據(jù)。在功能上可以代替以上四個(gè)輸出函數(shù)。,1發(fā)送數(shù)據(jù)的系統(tǒng)調(diào)用接口,write(),writev(),send(),sendto(),sendmsg(),sys_write(),sys_writev(),sys_socketcall(),sock_write(),sock_write(),sys_send(),sys_sendmsg(),sys_sendto(),sock_sendmsg(),inet_sendmsg(),系統(tǒng)調(diào)用,應(yīng)用層,BSD套接字接口,INET套接字(傳輸層),2.從INTE協(xié)議層到IP層,inet_sendmsg(),tcp_sendmsg(),tcp_sendmsg()tcp_transmit_skb(),ip_queue_xmit(),網(wǎng)絡(luò)層,BSD套接字接口,INET套接字(傳輸層),2.IP層到硬件層的數(shù)據(jù)發(fā)送過程,ip_queue_xmit2(),ip_output(),ip_finish_output(),ip_queue_xmit(),net/core/dev.c,IP層,dev_queue_xmit(),neigh_resolve_output_(),ip_finish_output2(),net/ipv4/ip_et/ipv4/ip_et/ipv4/ip_et/ipv4/ip_et/ipv4/ip_et/core/neighbour.c,數(shù)據(jù)鏈路與硬件層,4.硬件層的數(shù)據(jù)發(fā)送過程,dev_queue_xmit(),ei_start_xmit(),qdisc_run(),ei_start_xmit(),物理設(shè)備,硬件層,net/pkt_sched.h,硬件上有隊(duì)列嗎?,以太網(wǎng)卡(例如NE2000),6.2.4接收數(shù)據(jù),UNIX內(nèi)核為用戶提供的接收數(shù)據(jù)的系統(tǒng)調(diào)用也是5個(gè),它們分別是:1)read():與文件系統(tǒng)中的read()完全一致;2)readv():與read功能相似,所不同的是readv可以在一次函數(shù)調(diào)用中讀多個(gè)緩沖區(qū);3)recv():面向連接的接收數(shù)據(jù)過程(TCP);4)recvfrom():面向無連接的接收數(shù)據(jù)過程(UDP)5)recvmsg():直接使用msghdr結(jié)構(gòu)來接收數(shù)據(jù)。在功能上可以代替以上四個(gè)輸出函數(shù)。,1接收數(shù)據(jù)的系統(tǒng)調(diào)用接口,read(),readv(),recv(),recvfrom(),recvmsg(),sys_read(),sys_readv(),sys_socketcall(),sock_read(),sock_readv(),sys_recv(),sys_recvmsg(),sys_recvfrom(),sock_recvmsg(),inet_recvmsg(),系統(tǒng)調(diào)用,應(yīng)用層,BSD套接字接口,INET套接字(傳輸層),2.硬件層接收數(shù)據(jù)分析,netif_rx(),ei_recieve(),ei_interrupt(),ip_rev(),net/core/dev.cdrivers/net/8390.cdrivers/net/8309.c,硬件層,net_rx_action(),IP層,物理設(shè)備,NE網(wǎng)卡,向隊(duì)列寫入數(shù)據(jù),Backing接收數(shù)據(jù)隊(duì)列,從隊(duì)列中讀出數(shù)據(jù),2.硬件層接收數(shù)據(jù)分析,3.從IP層接收數(shù)據(jù),tcp_v4_rcv(),ip_local_deliver_finish(),ip_local_deliver(),tcp_rev_established(),INET層,net_rx_action(),ip_rev(),ip_rcv_finish(),net/ipv4/tcp_et/ipv4/ip_et/ipv4/ip_et/ipv4/input.c,硬件層,tcp_v4_do_rcv(),receive_queue隊(duì)列,IP層,4.從INTE層接收數(shù)據(jù),6.3套接字編程方法,1面向連接的數(shù)據(jù)流套接字時(shí)序步驟流套接字的服務(wù)器進(jìn)程和客戶機(jī)進(jìn)程在進(jìn)行通信前必須建立一條連接,其中,初始化連接的是客戶端進(jìn)程,接收連接的進(jìn)程是服務(wù)器端的進(jìn)程。建立連接和通信的主要步驟如下:(1)服務(wù)進(jìn)程首先調(diào)用Socket()創(chuàng)建一個(gè)流套接字;(2)服務(wù)進(jìn)程調(diào)用bind()公開服務(wù)器地址,將服務(wù)器地址與套接字綁定在一起;(3)服務(wù)進(jìn)程調(diào)用listen()將套接字轉(zhuǎn)換成傾聽套接字,此時(shí)該套接字可以接收來自客戶機(jī)的請求;(4)通過accept()阻塞服務(wù)進(jìn)程,此時(shí)該服務(wù)器進(jìn)入一個(gè)無限循環(huán),等待客戶進(jìn)程建立連接;(5)客戶進(jìn)程也通過Socket()創(chuàng)建一個(gè)流套接字,然后調(diào)用connect()與服務(wù)進(jìn)程建立連接;(6)當(dāng)客戶進(jìn)程的連接請求到達(dá)服務(wù)器后,服務(wù)進(jìn)程進(jìn)程被喚醒,生成一個(gè)新的套接字,服務(wù)進(jìn)程用這個(gè)新的套接字按預(yù)先定義的協(xié)議調(diào)用read()和write()進(jìn)行通信,處理客戶進(jìn)程的要求;而服務(wù)進(jìn)程最早生成的套接字則繼續(xù)用于監(jiān)聽網(wǎng)絡(luò)上的服務(wù)請求;(7)處理完成后,服務(wù)進(jìn)程和客戶進(jìn)程調(diào)用close()關(guān)閉這個(gè)連接和套接字;,服務(wù)器,Socket()、bind()、listen(),accept(),read(),write(),read(),close(),阻塞,等待客戶機(jī)連接請求,coonect(),write(),read(),socket(),close(),客戶,建立連接(TCP三路握手),發(fā)送數(shù)據(jù)請求,接收數(shù)據(jù),通信結(jié)束、關(guān)閉連接,面向連接的數(shù)據(jù)流套接字通信模型,2面向連接的數(shù)據(jù)流套接字的典型編程方法,(1)服務(wù)器一方main(void)if(創(chuàng)建一個(gè)流套接字返回值0)出錯(cuò)提示;退出;if(命名套接字返回值0)出錯(cuò)提示;退出;if(監(jiān)聽連接請求返回值0)出錯(cuò)提示;退出;for(;)新的套接描述符=取得第一個(gè)連接請求返回代碼;if(新的套接描述符0)出錯(cuò)提示;退出;接收數(shù)據(jù)信息;處理請求;將應(yīng)答發(fā)送給客戶機(jī);關(guān)閉套接字;,(2)客戶一方main(void)if(創(chuàng)建一個(gè)流套接字返回值0)出錯(cuò)提示;退出;if(連接服務(wù)器返回值0)出錯(cuò)提示;退出;for(;)發(fā)送數(shù)據(jù)信息;接收服務(wù)器方應(yīng)答;關(guān)閉套接字;,6.4無連接的數(shù)據(jù)流套接字的編程方法,1無連接的數(shù)據(jù)流套接字時(shí)序步驟數(shù)據(jù)報(bào)套接字的服務(wù)器進(jìn)程和客戶機(jī)進(jìn)程在進(jìn)行通信前不用建立連接。通信的主要步驟如下:(1)服務(wù)進(jìn)程首先調(diào)用Socket()創(chuàng)建一個(gè)數(shù)據(jù)報(bào)套接字;(2)服務(wù)進(jìn)程調(diào)用bind()將服務(wù)器地址綁定在在這個(gè)套接字上;(3)通過recvfrom()阻塞服務(wù)進(jìn)程,等待客戶進(jìn)程發(fā)來的請求;(4)客戶機(jī)首先調(diào)用Socket()創(chuàng)建一個(gè)數(shù)據(jù)報(bào)套接字;(5)客戶機(jī)進(jìn)程調(diào)用bind()將客戶機(jī)地址綁定在在此套接字上;(6)調(diào)用sendto(),客戶機(jī)進(jìn)程向服務(wù)進(jìn)程發(fā)出請求;(7)服務(wù)進(jìn)程接到客戶機(jī)數(shù)據(jù)報(bào)后被喚醒,執(zhí)行完客戶機(jī)請求后調(diào)用sendto()將處理結(jié)果返回給客戶機(jī);(8)客戶機(jī)調(diào)用recvfrom()接收服務(wù)進(jìn)程返回的請求處理結(jié)果;(9)服務(wù)進(jìn)程和客戶進(jìn)程調(diào)用close()撤消套接字;,2非連接的數(shù)據(jù)流套接字的通信模型,3非連接的數(shù)據(jù)流套接字的典型編程方法,(1)服務(wù)器一方main(void)if(創(chuàng)建一個(gè)數(shù)據(jù)報(bào)套接字返回值0)出錯(cuò)提示;退出;if(命名套接字返回值0)出錯(cuò)提示;退出;for(;)接收客戶機(jī)數(shù)據(jù)報(bào)(請求);處理請求;將數(shù)據(jù)(結(jié)果)發(fā)送給客戶機(jī);關(guān)閉套接字;,3非連接的數(shù)據(jù)流套接字的典型編程方法,(1)客戶端一方main(void)if(創(chuàng)建一個(gè)數(shù)據(jù)報(bào)套接字返回值0)出錯(cuò)提示;退出;if(命名該套接字返回值0)出錯(cuò)提示;退出;for(;)發(fā)送數(shù)據(jù)報(bào)(請求)給服務(wù)器;接收服務(wù)器應(yīng)答;關(guān)閉套接字;,6.5基于客戶/服務(wù)器模式的網(wǎng)絡(luò)編程,1客戶/服務(wù)器的工作流程(1)必須使用標(biāo)準(zhǔn)函數(shù)庫實(shí)現(xiàn)系統(tǒng)調(diào)用,以陷入內(nèi)核獲得完成用戶任務(wù)所需要的系統(tǒng)軟、硬件資源;(2)系統(tǒng)調(diào)用都被內(nèi)核入口點(diǎn)system_call函數(shù)截獲,該函數(shù)根據(jù)所傳遞的參數(shù)確定應(yīng)該執(zhí)行哪些系統(tǒng)調(diào)用,并通過檢查系統(tǒng)調(diào)用表,以確定相應(yīng)的服務(wù)例程。最后把控制權(quán)轉(zhuǎn)給該服務(wù)例程;(3)該服務(wù)過程立即和相關(guān)的內(nèi)核代碼模塊建立聯(lián)系。這些模塊可能進(jìn)一步需要和其他內(nèi)核模塊或者底層硬件通訊;(4)當(dāng)系統(tǒng)調(diào)用結(jié)束后,結(jié)果按照相同的路徑反方向返回。核心把控制交給用戶程序;(5)如果該實(shí)例有某些錯(cuò)誤的操作,如用戶堆棧溢出,處理器將引發(fā)一個(gè)異常通知內(nèi)核,有內(nèi)核執(zhí)行相應(yīng)的處理程序來處理異常事件。,客戶端,服務(wù)器端,解析域名生成服務(wù)器請求計(jì)算并顯示服務(wù)內(nèi)容和圖像,Buffetallocted,Socketbuffer,客戶程序,系統(tǒng)調(diào)用,Connect()recv()send()fputs(),TCP/IP協(xié)議,內(nèi)核,NIC緩沖,網(wǎng)卡,解析域名生成服務(wù)器請求計(jì)算并顯示服務(wù)內(nèi)容和圖像,Buffetallocted,Socketbuffer,TCP/IP協(xié)議,NIC緩沖,accept()recv()send()open()read(),文件系統(tǒng)RAMorDisk,服務(wù)器程序,內(nèi)核,網(wǎng)卡,HTTPTCPIP,網(wǎng)絡(luò)協(xié)議,Internet,操作系統(tǒng)支持Web服務(wù)器的工作流程,1客戶/服務(wù)器的工作流程,在客戶端,如果運(yùn)行的是Web測覽器,當(dāng)向Web瀏覽器的“地址”窗口處輸入http:/.或者“WWW.”等形式的網(wǎng)址并回車,就是由Web瀏覽器向web服務(wù)器發(fā)出的一次客戶請求。該請求經(jīng)過解析后,通過系統(tǒng)調(diào)用由用戶態(tài)轉(zhuǎn)為核心態(tài)執(zhí)行。在核心態(tài)操作系統(tǒng)中的TCP/IP協(xié)議代碼和網(wǎng)卡驅(qū)動(dòng)程序控制網(wǎng)卡把請求發(fā)送到相應(yīng)的網(wǎng)絡(luò)上,等待Web服務(wù)器響應(yīng)。當(dāng)服務(wù)響應(yīng)返回時(shí),由網(wǎng)卡接收,并通過內(nèi)核傳送給客戶程序。在服務(wù)器端,內(nèi)核通過網(wǎng)卡從網(wǎng)絡(luò)上接收Web請求,并通過系統(tǒng)調(diào)用傳遞給Web服務(wù)器。Web服務(wù)器根據(jù)此服務(wù)請求執(zhí)行相應(yīng)的服務(wù)過程,并由內(nèi)核把結(jié)果放到網(wǎng)絡(luò)上傳送給客戶。Web瀏覽器和Web服務(wù)器使用URL和URLConnection進(jìn)行網(wǎng)絡(luò)通信,這是一種較高層次的網(wǎng)絡(luò)通信,為的是訪問Internet上的資源。通常,用戶也常常需要編寫一個(gè)由操作系統(tǒng)提供的接口客戶/服務(wù)器應(yīng)用程序,由套接口實(shí)現(xiàn)的客戶服務(wù)器應(yīng)用程序是低級(jí)別的網(wǎng)絡(luò)通信。因此,從程序員的觀點(diǎn)來看,操作系統(tǒng)所提供的系統(tǒng)調(diào)用定義了應(yīng)用程序和協(xié)議棧之間的接口。應(yīng)用程序無論使用哪種通信協(xié)議進(jìn)行網(wǎng)絡(luò)通信,都必須申請同操作系統(tǒng)交互才能得到服務(wù)。,#include#include#include#include#include#include#include#includeintmain(intargc,char*argv)intsockfd,numb,port=8000;structsockaddr_ins;structhostent*host;charbuf100;if(argc!=2)fprintf(stderr,usage:%shostnamen,argv0);exit(1);if(!(host=gethostbyname(argv1)perror(errorinresolvinghostname);exit(1);sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd0)exit(0);/*父進(jìn)程退出,使shell成為前臺(tái)進(jìn)程*/setsid();/*第1子進(jìn)程成為新會(huì)話和新進(jìn)程組的領(lǐng)頭進(jìn)程的同時(shí)也失去控制終端*/*第1子進(jìn)程執(zhí)行下面的操作*/for(i=0;i0)/*接收來自服務(wù)器的字符,放在緩沖區(qū)buf*/bufn=0;if(fputs(buf,stdout)=EOF)perror(Errorfputsn);exit(1);if(n0)/*顯示來自服務(wù)器的字符信息*/printf(nResponsefromserver:nn%sn,buf);exit(1);close(sockfd);/*通信目的完成,關(guān)閉與服務(wù)器的連接*/,5編制TCP服務(wù)器程序,客戶機(jī)程序是一個(gè)單個(gè)用戶運(yùn)行來訪問遠(yuǎn)程服務(wù)器的程序,它要求只經(jīng)過很短的延時(shí)、最好沒有延時(shí)就能得到答復(fù)。當(dāng)從提供服務(wù)的主機(jī)的角度去考慮問題時(shí),情況將變得非常復(fù)雜。因?yàn)榉植荚诟鞯氐挠脩艨赡茉谕粫r(shí)刻訪問同一臺(tái)給定的服務(wù)器,每個(gè)用戶都期望得到無時(shí)延的響應(yīng)。為了盡最大可能滿足所有用戶的要求,提供應(yīng)用服務(wù)的主機(jī)必須能夠快速響應(yīng)并處理多個(gè)請求,因此,服務(wù)器必須使用并發(fā)處理。由于用戶對并發(fā)的需要使得服務(wù)器程序的設(shè)計(jì)、編程和維護(hù)都趨向復(fù)雜化,需要使用新的算法和新的編程技術(shù)。衡量一個(gè)服務(wù)器程序的標(biāo)準(zhǔn)同樣是可靠性。好用和高吞吐率。下面先介紹一種面向連接的、循環(huán)串行處理客戶請求的服務(wù)器算法和編程技術(shù)。它是服務(wù)器編程技術(shù)中最簡單的,因此稱它為一個(gè)簡單的TCP服務(wù)器程序。,編制TCP服務(wù)器程序,(2)一個(gè)簡單TCP服務(wù)器的編程方法所謂簡單的TCP服務(wù)器程序是指一個(gè)單進(jìn)程一次只處理一個(gè)客戶請求的服務(wù)器程序。設(shè)計(jì)一個(gè)簡單TCP服務(wù)器程序的步驟如下:l)調(diào)用socket()創(chuàng)建一個(gè)套接口。2)調(diào)用bind()把套接口綁定到自己所提供服務(wù)的知名端口上。3)調(diào)用listen()將套接口設(shè)置為被動(dòng)模式,使之準(zhǔn)備接受外來的連接請求。4)調(diào)用accept()接受下一個(gè)連接請求,并獲得連接的一個(gè)新套接口。5)讀取來自客戶機(jī)的請求,并處理該客戶請求,然后向客戶發(fā)回響應(yīng)數(shù)據(jù)。6)當(dāng)與該客戶完成通信時(shí),關(guān)閉此次連接,并返回步驟等待接受下一個(gè)新的連接請求。,編制TCP服務(wù)器程序,(3)一個(gè)簡單TCP服務(wù)器的實(shí)現(xiàn)以下給出的程序server.c是為client.c客戶程序提供服務(wù)的服務(wù)器程序。程序server.c在接收到客戶詢問當(dāng)前時(shí)間的請求之后,便處理這個(gè)請求,并將結(jié)果發(fā)回給客戶。這個(gè)程序允許接納20個(gè)服務(wù)請求的輸入隊(duì)列。它必須和client.c程序一道工作,先啟動(dòng)server.c,稍后啟動(dòng)client.c程序。#include#include#include#include#include#includeintport=8000;,voidmain()structsockaddr_inserveraddr,clientaddr;/*服務(wù)器和客戶端地址信息*/intsockfd,temp_sockfd,clientaddr_size;charbuf16384;time_tticks;sockfd=socket(AF_INET,SOCK_STREAM,0);/*創(chuàng)建IPv4的數(shù)據(jù)流socket*/if(sockfd=-1)perror(calltosocket);exit(1);bzero(/*指定服務(wù)器用于監(jiān)聽的端口*/,if(bind(sockfd,(structsockaddr*),if(read(temp_sockfd,buf,16384)=-1)/*由子進(jìn)程提供服務(wù)*/perror(calltoread);exit(1);printf(receivedfromclient:%sn,buf);ticks=time(NULL);sprintf(buf,%srn,ctime(,程序的分析,程序一開始先調(diào)用socket()創(chuàng)建一個(gè)套接口,然后調(diào)用bind()函數(shù)將知名端口13捆綁到這個(gè)套接回。程序中指定IP地址為INADDRANY,它意味著允許服務(wù)器在任意端口上接收來自客戶的連接請求。通過調(diào)用listen()函數(shù)將此套接口轉(zhuǎn)變成一個(gè)監(jiān)聽套接口,以便使內(nèi)核開始監(jiān)聽連接到13號(hào)端口上的客戶連接請求。socket()、bind()和listen()是所有TCP服務(wù)器監(jiān)聽客戶連接所必須的“三步曲”。接著server.c進(jìn)入一個(gè)無限循環(huán)等待來自客戶的連接請求。一般情況下,服務(wù)器進(jìn)程在調(diào)用accept()函數(shù)之后處于阻塞狀態(tài)。等待著客戶連接請求的到達(dá)。當(dāng)TCP連接的三路握手信號(hào)結(jié)束時(shí),accept()返回,它的返回值是內(nèi)核為剛連接到服務(wù)器的客戶創(chuàng)建的一個(gè)新的、已連接的套接口描述符特temp_sockfd。隨后,函數(shù)read()使用這個(gè)套接口描述符接收來自客戶端的服務(wù)請求;并打印這個(gè)信息。接著執(zhí)行請求服務(wù),調(diào)用time()函數(shù)獲取當(dāng)前時(shí)間和日期,并用函數(shù)ctime()將這個(gè)時(shí)間(秒數(shù))轉(zhuǎn)變成人們習(xí)慣的閱讀格式。最后調(diào)用write()函數(shù),將時(shí)間數(shù)據(jù)發(fā)回客戶端。,程序的分析,程序一開始先調(diào)用socket()創(chuàng)建一個(gè)套接口,然后調(diào)用bind()函數(shù)將知名端口13捆綁到這個(gè)套接回。程序中指定IP地址為INADDRANY,它意味著允許服務(wù)器在任意端口上接收來自客戶的連接請求。通過調(diào)用listen()函數(shù)將此套接口轉(zhuǎn)變成一個(gè)監(jiān)聽套接口,以便使內(nèi)核開始監(jiān)聽連接到13號(hào)端口上的客戶連接請求。socket()、bind()和listen()是所有TCP服務(wù)器監(jiān)聽客戶連接所必須的“三步曲”。接著server.c進(jìn)入一個(gè)無限循環(huán)等待來自客戶的連接請求。一般情況下,服務(wù)器進(jìn)程在調(diào)用accept()函數(shù)之后處于阻塞狀態(tài)。等待著客戶連接請求的到達(dá)。當(dāng)TCP連接的三路握手信號(hào)結(jié)束時(shí),accept()返回,它的返回值是內(nèi)核為剛連接到服務(wù)器的客戶創(chuàng)建的一個(gè)新的、已連接的套接口描述符特temp_sockfd。隨后,函數(shù)read()使用這個(gè)套接口描述符接收來自客戶端的服務(wù)請求;并打印這個(gè)信息。接著執(zhí)行請求服務(wù),調(diào)用time()函數(shù)獲取當(dāng)前時(shí)間和日期,并用函數(shù)ctime()將這個(gè)時(shí)間(秒數(shù))轉(zhuǎn)變成人們習(xí)慣的閱讀格式。最后調(diào)用write()函數(shù),將時(shí)間數(shù)據(jù)發(fā)回客戶端。,6并發(fā)服務(wù)器的設(shè)計(jì),server.c程序稱為面向連接的循環(huán)服務(wù)器或稱為面向連接的迭代服務(wù)器。它一次只能處理一個(gè)客戶,當(dāng)有多個(gè)客戶請求同時(shí)到達(dá)時(shí),要用listen()函數(shù)中第二個(gè)參數(shù),它是內(nèi)核允許接入的最大排隊(duì)數(shù)目。在這個(gè)隊(duì)列中,內(nèi)核每次返回一個(gè)給accept()函數(shù)。在這個(gè)例子中服務(wù)器的服務(wù)響應(yīng)速度是非??斓?,因?yàn)樗诜?wù)期間只執(zhí)行了兩個(gè)庫函數(shù)。如果服務(wù)器服務(wù)的項(xiàng)目是費(fèi)時(shí)的操作,其他排隊(duì)等待的客戶勢必要等待很長時(shí)間,因此就必須重新尋求新的方式,以便能夠同時(shí)為到達(dá)的多個(gè)客戶請求進(jìn)行服務(wù)。這種能夠同時(shí)為多個(gè)客戶進(jìn)行服務(wù)的程序稱為并發(fā)服務(wù)器。并發(fā)服務(wù)器能同時(shí)處理多個(gè)客戶請求,下面給出的實(shí)例是在并發(fā)服務(wù)器編程中使用fork()函數(shù)為每個(gè)到達(dá)的客戶派生一個(gè)子進(jìn)程,由這個(gè)子進(jìn)程處理客戶的請求。除此之外,還有許多其他編寫并發(fā)服務(wù)器的技術(shù),比如使用線程代替fork()調(diào)用,或在服務(wù)器運(yùn)行前預(yù)先執(zhí)行fork(),創(chuàng)建一定數(shù)量的子進(jìn)程,等等。,6并發(fā)服務(wù)器的設(shè)計(jì),并發(fā)實(shí)現(xiàn):可以通過進(jìn)程并發(fā)或線程并發(fā)實(shí)現(xiàn)。并發(fā)進(jìn)程是當(dāng)服務(wù)器收到客戶請求并accept()后,調(diào)用fork派生一個(gè)子進(jìn)程來為該客戶程序服務(wù),自己則回到等待狀態(tài),準(zhǔn)備接收下一個(gè)連接請求,子進(jìn)程則在服務(wù)完成后退出。并發(fā)進(jìn)程為每個(gè)客戶均fork一個(gè)子進(jìn)程,即每客戶單進(jìn)程服務(wù),子進(jìn)程可以即時(shí)派生,也可以預(yù)先派生一定的數(shù)量以備系統(tǒng)調(diào)用。并發(fā)線程是指當(dāng)有客戶連接時(shí)由主線程創(chuàng)建子線程為客戶提供服務(wù),這種方法的執(zhí)行效率更高。下面只對并發(fā)進(jìn)程設(shè)計(jì)作詳細(xì)介紹。,(2)并發(fā)進(jìn)程實(shí)現(xiàn)在介紹編寫并發(fā)服務(wù)程序前,首先來了解一下服務(wù)器并發(fā)進(jìn)程是如何實(shí)現(xiàn)的,也就是理解UNIX的fork函數(shù)。fork()是UNIX中派生子進(jìn)程的唯一方法,在調(diào)用中系統(tǒng)將從父進(jìn)程虛空間到子進(jìn)程虛空間的拷貝,兩個(gè)進(jìn)程的代碼段和用戶數(shù)據(jù)段是完全相同的,并且兩個(gè)進(jìn)程的系統(tǒng)數(shù)據(jù)段也幾乎相同。但是它們有各自的數(shù)據(jù)結(jié)構(gòu)且進(jìn)程標(biāo)志符是不同的。調(diào)用格式為:pid_tfork(pid);/*返回:在子進(jìn)程中為0;在父進(jìn)程中為子進(jìn)程的ID;-1出錯(cuò)*/,fork函數(shù)調(diào)用一次卻返回兩個(gè)不同的值,是因?yàn)閒ork()以后的某個(gè)時(shí)刻,子進(jìn)程創(chuàng)建后當(dāng)前運(yùn)行的仍然是父進(jìn)程。因此,子進(jìn)程的上下文被保存,并進(jìn)入到就緒隊(duì)列中等待調(diào)度。因此,子進(jìn)程的上下文被保存,并進(jìn)入到就緒隊(duì)列中等待調(diào)度。當(dāng)父進(jìn)程運(yùn)行結(jié)束后將會(huì)返回子進(jìn)程的標(biāo)識(shí)符,而父進(jìn)程也將從核心態(tài)轉(zhuǎn)化成用戶態(tài)。當(dāng)子進(jìn)程得到調(diào)度并投入運(yùn)行后,由于子進(jìn)程與父進(jìn)程的代碼段相同,子進(jìn)程同樣也調(diào)用fork(),只不過不真正創(chuàng)建子進(jìn)程,只是返回一個(gè)0值,然后子進(jìn)程在自己的虛空間中運(yùn)行。我們可以通過返回值是否為0判斷當(dāng)前進(jìn)程是父進(jìn)程還是子進(jìn)程。值得注意的是,父進(jìn)程在fork之前打開的所有描述字在fork后均與子進(jìn)程共享,并發(fā)服務(wù)程序設(shè)計(jì)正是利用這一特性實(shí)現(xiàn)的。當(dāng)服務(wù)器accept連接,并調(diào)用fork后,已經(jīng)連接的套接口就在父進(jìn)程與子進(jìn)程間得到共享,此時(shí)套接口描述符中訪問計(jì)數(shù)項(xiàng)的記錄為2,表示該套接口被兩個(gè)進(jìn)程訪問。此后父進(jìn)程關(guān)閉,訪問計(jì)數(shù)減為1,因此,子進(jìn)程還在繼續(xù)訪問該套接口并為該口的連接客戶提供服務(wù)。只有再次close,計(jì)數(shù)變?yōu)?該套接口的連接才真正被關(guān)閉。,#include#include#include#include#include#includeintport=8000;voidmain()structsockaddr_inserveraddr,clientaddr;/*服務(wù)器和客戶端地址信息*/intsockfd,temp_sockfd,clientaddr_size;charbuf16384;time_tticks;pid_tpid;sockfd=socket(AF_INET,SOCK_STREAM,0);/*創(chuàng)建IPv4的數(shù)據(jù)流socket*/if(sockfd=-1)perror(calltosocket);exit(1);,bzero(,while(1)/*服務(wù)器無限循環(huán)等待客戶連接請求*/temp_sockfd=accept(sockfd,(structsockaddr*),編譯并運(yùn)行client.c和serverf.c,顯示執(zhí)行結(jié)果。$gcc-oclientclient.c$gcc-oserverfserverf.c先運(yùn)行serverf服務(wù)器程序。屏幕顯示結(jié)果如下:$./serverfAccoptingconnectionsReceivedfromclient:Whatisthetimeanddate打開第二個(gè)窗口,運(yùn)行client客戶程序。屏幕顯示結(jié)果下:$./clientSendingmessageWhatisthetimeanddatatoserver.sentmessagewaitforresponseMonDec911:20:422002,6.6UDP套接字編成的基本方法,用戶數(shù)據(jù)報(bào)協(xié)議UDP是在傳輸性能上比TCP更低級(jí)的協(xié)議,它和TCP協(xié)議存在著本質(zhì)的差異。因?yàn)閁DP不面向連接所以它不提供由保證的信息傳輸,更不提供有保證的傳輸錯(cuò)誤提示。除此之外,UDP也不能保證消息以被發(fā)送時(shí)的順序到達(dá)。而TCP是面向連接的,因此它提供可靠的宇節(jié)流等許多保證。但是在某些情況下,使用TCP協(xié)議不一定是最佳選擇,因?yàn)門CP的連接和終止有交換7個(gè)分組的額外開銷。如果使用UDP,那么只有兩個(gè)分組需要交換:請求和應(yīng)答。這樣在發(fā)送的數(shù)據(jù)塊不是很大的情況下,有些應(yīng)用程序可以使用UDP協(xié)議。例如:可以將傳輸?shù)臄?shù)據(jù)放在一個(gè)物理UDP數(shù)據(jù)報(bào)中,并允許一些傳輸數(shù)據(jù)丟失。只要丟失的數(shù)據(jù)不損壞所傳輸?shù)男畔⒌耐暾约纯?,因此這部分丟失的數(shù)據(jù)就不需要重發(fā)。,6.6UDP套接字編成的基本方法,DNS域名系統(tǒng)、NFS網(wǎng)絡(luò)文件系統(tǒng)和SNMP(簡單網(wǎng)絡(luò)管理協(xié)議)等都使用的是UDP協(xié)議。通常,在局域網(wǎng)上UDP數(shù)據(jù)包很少丟失。在選擇UDP還是是TCP傳輸數(shù)據(jù)時(shí),還要考慮另外一個(gè)因素,即通信應(yīng)用程序是否需要廣播或組播通信。面向連接的TCP協(xié)議之提供點(diǎn)到點(diǎn)通信,而不能提供廣播或組播通信;所以當(dāng)然需要廣播或組播通信服務(wù)時(shí),需要使用UDP的無連接服務(wù)。下圖給出了使用UDP編寫客戶服務(wù)器程序所需要的基本函數(shù)。在客戶端不需要調(diào)用函數(shù)connect()與服務(wù)器建立連接,它直接調(diào)用sendto()函數(shù)向服務(wù)器發(fā)送數(shù)據(jù)就可以了。同樣在服務(wù)器端也不需要監(jiān)聽套接口等待用戶的連接請求,它直接調(diào)用recvfrom()函數(shù)等待客戶數(shù)據(jù)的到達(dá)。,socket(),sendto(),recvfrom(),close(),sendto(),處理客戶請求,recvfrom(),socket()bind(),阻塞,等待收到客戶的數(shù)據(jù),應(yīng)答,請求,UDP客戶,UDP服務(wù)器,UDP客戶/服務(wù)器的程序流程示意圖,1UDP程序使用的套接口函數(shù),從上圖中可以觀察到一般UDP套接口無論是客戶端還是服務(wù)器端都不能使用connect()函數(shù),那樣只能與一個(gè)特定的遠(yuǎn)程計(jì)算機(jī)和端點(diǎn)通信,服務(wù)器就不能使用一個(gè)套接口接收來自任意客戶機(jī)的數(shù)據(jù)報(bào)。所以UDP服務(wù)器使用的套接口只能是一個(gè)非連接的,客戶和服務(wù)器雙方直接使用sendto()向?qū)Ψ桨l(fā)送請求和數(shù)據(jù)。它的函數(shù)原形如下:#includessize_tsendto(intsockfd,constvoid*buf,size_tlen,intflags,conststructsockaddr*to,socklen_taddrlen);第一個(gè)參數(shù)sockfd是由socket()函數(shù)返回的套接口描述符,第二個(gè)參數(shù)buf是指向存放發(fā)送數(shù)據(jù)的緩沖區(qū)指針;第三個(gè)參數(shù)len指明緩沖區(qū)中的字節(jié)數(shù);第四個(gè)參數(shù)flags表明排錯(cuò)或者控制選項(xiàng);第五個(gè)參數(shù)to是一個(gè)指向sockaddr_in結(jié)構(gòu)的指針,結(jié)構(gòu)中含有將報(bào)文要發(fā)往的IP地址和端口號(hào);最后一個(gè)參數(shù)addrlen指明這個(gè)地址結(jié)構(gòu)的大小。,1UDP程序使用的套接口函數(shù),在UDP套接口編程中使用的另一個(gè)函數(shù)是recvfrom(),它可以使客戶服務(wù)器雙方接收對方的請求和數(shù)據(jù)。它的函數(shù)原形如下:#includessize_trecvfrom(intsockfd,void*buf,size_tlen,intflags,structsockaddr*from,socklen_t*addrlen

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(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ǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論