網(wǎng)絡(luò)編程實用教程第2章 UNIX中的套接字網(wǎng)絡(luò)編程接口_第1頁
網(wǎng)絡(luò)編程實用教程第2章 UNIX中的套接字網(wǎng)絡(luò)編程接口_第2頁
網(wǎng)絡(luò)編程實用教程第2章 UNIX中的套接字網(wǎng)絡(luò)編程接口_第3頁
網(wǎng)絡(luò)編程實用教程第2章 UNIX中的套接字網(wǎng)絡(luò)編程接口_第4頁
網(wǎng)絡(luò)編程實用教程第2章 UNIX中的套接字網(wǎng)絡(luò)編程接口_第5頁
已閱讀5頁,還剩87頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第2章UNIX中的套接字網(wǎng)絡(luò)編程接口UNIX套接字網(wǎng)絡(luò)編程接口的產(chǎn)生與發(fā)展過程套接字與UNIX操作系統(tǒng)的關(guān)系套接字編程的基本概念套接字的特點、應(yīng)用場合、使用的數(shù)據(jù)類型面向連接的套接字編程套接字的工作過程、系統(tǒng)調(diào)用、編程實例借助實例分析進(jìn)程的阻塞問題和對策無連接的套接字編程無連接套接字編程的兩種模式(C/S和對等)數(shù)據(jù)報套接字的對等模式編程實例

2.1UNIX套接字網(wǎng)絡(luò)編程接口的產(chǎn)生與發(fā)展2.1.1問題的提出應(yīng)用程序與協(xié)議軟件進(jìn)行交互時須說明許多細(xì)節(jié):是服務(wù)器還是客戶機(jī),主動還是被動通信?發(fā)送方需說明發(fā)送的數(shù)據(jù);接收方需說明接收的數(shù)據(jù)如何存放。站在應(yīng)用程序?qū)崿F(xiàn)的角度,應(yīng)用程序如何方便地使用協(xié)議棧軟件進(jìn)行通信呢?如果能在應(yīng)用程序與協(xié)議棧軟件之間提供一個軟件接口,就可以方便客戶與服務(wù)器軟件的編程。UNIX最早將TCP/IP協(xié)議簇集成到內(nèi)核中,UNIX的開發(fā)者提出并實現(xiàn)了套接字應(yīng)用編程接口。套接字應(yīng)用程序編程接口:是網(wǎng)絡(luò)應(yīng)用程序通過網(wǎng)絡(luò)協(xié)議棧進(jìn)行通信時所使用的接口,即應(yīng)用程序與協(xié)議棧軟件之間的接口,簡稱套接字編程接口(SocketAPI)。定義了應(yīng)用程序與協(xié)議棧軟件進(jìn)行交互時可以使用的一組操作,決定了應(yīng)用程序使用協(xié)議棧的方式、應(yīng)用程序所能實現(xiàn)的功能、以及開發(fā)具有這些功能的程序的難度。套接字編程接口給出了應(yīng)用程序能夠調(diào)用的一組過程,以及這些過程所需的參數(shù),每個獨立的過程完成一個與協(xié)議棧軟件交互的基本操作(如:建立連接、接收數(shù)據(jù)、釋放鏈接)。2.1.2套接字編程接口的起源與應(yīng)用加州大學(xué)伯克利分校開發(fā)了一個包括TCP/IP協(xié)議簇的BSDUNIX,并迅速得到推廣,套接字編程接口是這個操作系統(tǒng)的一部分。TCP/IP標(biāo)準(zhǔn)并沒有定義應(yīng)用程序用來與該協(xié)議進(jìn)行交互的應(yīng)用程序編程接口,只規(guī)定了應(yīng)該提供的一般操作,并允許各個操作系統(tǒng)去定義用來實現(xiàn)這些操作的具體API。一個協(xié)議標(biāo)準(zhǔn)可能只是建議某個操作在應(yīng)用程序發(fā)送數(shù)據(jù)時是需要的,而由應(yīng)用程序編程接口來定義具體的函數(shù)名和每個參數(shù)的類型。盡管協(xié)議標(biāo)準(zhǔn)允許操作系統(tǒng)設(shè)計者開發(fā)自己的應(yīng)用程序編程接口,但由于BSDUNIX的廣泛使用,后來的許多操作系統(tǒng)及編程語言都選擇了對套接字編程接口的支持。由于這個套接字規(guī)范最早是由Berkeley大學(xué)開發(fā)的,一般將它稱為BerkeleySockets規(guī)范。BerkeleySockets規(guī)范規(guī)定了一系列與套接字使用有關(guān)的庫函數(shù),為在UNIX操作系統(tǒng)下不同計算機(jī)中的應(yīng)用程序進(jìn)程之間,使用TCP/IP協(xié)議簇進(jìn)行網(wǎng)絡(luò)通信提供了一套應(yīng)用程序編程接口。2.1.3套接字編程接口的兩種實現(xiàn)方式采用兩種實現(xiàn)套接字編程接口的方式:在操作系統(tǒng)的內(nèi)核中增加相應(yīng)的軟件來實現(xiàn);通過開發(fā)操作系統(tǒng)之外的函數(shù)庫來實現(xiàn)。

在BSDUNIX及起源于它的操作系統(tǒng)中,套接字函數(shù)是操作系統(tǒng)本身的功能調(diào)用,是操作系統(tǒng)內(nèi)核的一部分。其他操作系統(tǒng)供應(yīng)商為了不修改基本操作系統(tǒng),開發(fā)了套接字庫(SocketLibrary)來提供套接字編程接口。套接字庫中的每個過程具有與UNIX套接字函數(shù)相同的名字與參數(shù),向沒有本機(jī)套接字的操作系統(tǒng)上的應(yīng)用程序提供套接字編程接口。套接字庫與操作系統(tǒng)內(nèi)核中實現(xiàn)的套接字在語義上是相同的。應(yīng)用程序調(diào)用套接字過程不必管它是由操作系統(tǒng)內(nèi)核過程提供的,還是由庫過程提供的。這種方式提供了程序的可移植性:將程序從一臺計算機(jī)移植到另一臺計算機(jī)時,程序的源代碼不需改動,只要用新計算機(jī)上的套接字庫重新編譯即可。套接字庫與操作系統(tǒng)直接提供的本機(jī)套接字編程接口在實現(xiàn)上是不同的。套接字庫的過程需要鏈接到應(yīng)用程序中;并駐留于應(yīng)用程序地址空間;調(diào)用時控制從應(yīng)用程序轉(zhuǎn)向庫程序,并進(jìn)一步調(diào)用一個或多個底層操作系統(tǒng)的功能調(diào)用。2.1.4套接字通信與UNIX操作系統(tǒng)的輸入/輸出套接字編程接口采用傳統(tǒng)的UNIX輸入/輸出模式。UNIX操作系統(tǒng)對文件和所有其它的輸入/輸出設(shè)備采用一種統(tǒng)一的操作模式,就是“打開-讀-寫-關(guān)閉”(open-read-write-close)的I/O模式。調(diào)用open命令獲得對指定文件或設(shè)備的使用權(quán),并返回一個用來標(biāo)識該文件或設(shè)備的短整型描述符,作為用戶在打開的文件或設(shè)備上進(jìn)行I/O的句柄。多次調(diào)用read或write命令來傳輸數(shù)據(jù),期間用描述符作為命令的參數(shù),指明要操作的對象。傳輸完成后調(diào)用close命令,通知操作系統(tǒng)已經(jīng)完成了對某對象的調(diào)用,釋放所占用的資源。當(dāng)TCP/IP協(xié)議被集成到UNIX內(nèi)核中的時候,相當(dāng)于在UNIX系統(tǒng)中引入了一種新型的I/O操作。應(yīng)用程序通過網(wǎng)絡(luò)協(xié)議棧來交換數(shù)據(jù)。操作系統(tǒng)和應(yīng)用程序都將套接字編程接口也看作一種輸入/輸出機(jī)制。操作過程類似,創(chuàng)建套接字-使用-刪除;操作方法類似,申請生成一個套接字后,系統(tǒng)返回一個短整型描述符來標(biāo)識這個套接字對象;使用的過程名可以是相同的,如write過程可以將數(shù)據(jù)發(fā)送給另一個程序、文件或網(wǎng)絡(luò)中的另一個進(jìn)程。UNIX系統(tǒng)對于各種I/O的集成提供了靈活性。應(yīng)用程序可以編寫成向任何地方傳輸數(shù)據(jù),取決于描述符對象代表什么(設(shè)備、文件、套接字)。系統(tǒng)對套接字和其它I/O使用相同的描述符空間,使得單個應(yīng)用程序既可以進(jìn)行網(wǎng)絡(luò)通信,又可以實現(xiàn)本地數(shù)據(jù)傳輸。用戶進(jìn)程與網(wǎng)絡(luò)協(xié)議的交互,實際要比用戶進(jìn)程與傳統(tǒng)I/O設(shè)備的交互復(fù)雜得多。僅提供open、read、write、close四個過程遠(yuǎn)不夠.使用套接字的應(yīng)用程序必須說明許多細(xì)節(jié)。應(yīng)用程序使用的協(xié)議簇、遠(yuǎn)程計算機(jī)的地址、該應(yīng)用程序是客戶機(jī)還是服務(wù)器、希望的服務(wù)類型是面向連接的還是無連接的,等等。為了提供這些細(xì)節(jié),每個套接字有許多參數(shù)與選項,需要應(yīng)用程序提供具體值。為避免單個套接字函數(shù)參數(shù)過多,套接字編程接口的設(shè)計者定義了多個函數(shù)。例如,創(chuàng)建套接字時,先調(diào)用一個函數(shù)創(chuàng)建套接字,再調(diào)用其它函數(shù)說明使用套接字的細(xì)節(jié)。2.2套接字編程的基本概念圖2.1電插座與電話插座的作用2.2.1什么是套接字(SOCKET)套接字是對網(wǎng)絡(luò)中不同主機(jī)上應(yīng)用進(jìn)程之間進(jìn)行雙向通信的端點的抽象。一個套接字就是網(wǎng)絡(luò)上進(jìn)程通信的一端,提供了應(yīng)用層進(jìn)程利用網(wǎng)絡(luò)協(xié)議棧交換數(shù)據(jù)的機(jī)制。

圖2.2應(yīng)用進(jìn)程、套接口、網(wǎng)絡(luò)協(xié)議棧及操作系統(tǒng)的關(guān)系兩個應(yīng)用進(jìn)程只要分別連接到自己的套接字,就可以通過網(wǎng)絡(luò)痛惜,不用去管復(fù)雜的網(wǎng)絡(luò)結(jié)構(gòu)及數(shù)據(jù)傳輸過程。從多個層面來理解套接字這個概念的內(nèi)涵從所處的地位來講,套接字上聯(lián)應(yīng)用進(jìn)程,下聯(lián)網(wǎng)絡(luò)協(xié)議棧,是應(yīng)用程序通過網(wǎng)絡(luò)協(xié)議棧進(jìn)行通信的接口,是應(yīng)用程序與網(wǎng)絡(luò)協(xié)議棧交互的接口.從實現(xiàn)的角度來講,非常復(fù)雜。套接字是一個復(fù)雜的軟件機(jī)構(gòu),包含了一定的數(shù)據(jù)結(jié)構(gòu),包含許多選項,由操作系統(tǒng)內(nèi)核管理。從使用的角度來講,非常簡單。對套接字的操作形成了一種網(wǎng)絡(luò)應(yīng)用程序的編程接口(API),提供了一組系統(tǒng)調(diào)用或庫函數(shù),可以用于構(gòu)造套接字、安裝綁定套接字、連接套接字、通過套接字交換數(shù)據(jù)、關(guān)閉套接字,實現(xiàn)各種分布式應(yīng)用。套接字編程接口是一套操作套接字的編程接口函數(shù),套接字是它的操作對象。2.2.2套接字的特點1.通信域通信域:是一個計算機(jī)網(wǎng)絡(luò)的范圍,在這個范圍中,所有的計算機(jī)使用同一種網(wǎng)絡(luò)體系結(jié)構(gòu)及協(xié)議棧。套接字存在于通信域中。套接字通常只和同一域中的套接字交換數(shù)據(jù)。如果數(shù)據(jù)交換要穿越域的邊界,就一定要執(zhí)行某種解釋程序。這里,僅僅針對Internet域,并且使用Internet協(xié)議族(即TCP/IP協(xié)議族)來通信。2.套接字具有三種類型每一個正被使用的套接字都有它確定的類型。只有相同類型的套接字才能相互通信。(1)數(shù)據(jù)報套接字(DatagramSOCKET)數(shù)據(jù)報套接字提供無連接的、不保證可靠的、獨立的數(shù)據(jù)報傳輸服務(wù)。在Internet通信域中,數(shù)據(jù)報套接字使用UDP數(shù)據(jù)報協(xié)議形成的進(jìn)程間通路,具有UDP協(xié)議為上層所提供的服務(wù)的所有特點。一般用于輕載通信,并具有多播通信的能力。圖2.3在Internet通信域中,數(shù)據(jù)報套接字基于UDP協(xié)議(2)流式套接字(StreamSOCKET)流式套接字提供雙向的、有序的、無重復(fù)的、無記錄邊界的可靠的數(shù)據(jù)流傳輸服務(wù)。在Internet通信域中,流式套接字使用TCP協(xié)議形成的進(jìn)程間通路,具有TCP協(xié)議為上層所提供的服務(wù)的所有特點。在使用流式套接字傳輸數(shù)據(jù)之前,必須在數(shù)據(jù)的發(fā)送端和接收端之間建立連接。一般用于交換大批量的數(shù)據(jù)時,或者要求數(shù)據(jù)按照發(fā)送的順序無重復(fù)的到達(dá)目的地的時候。圖2.4在Internet通信域中,流式套接字基于TCP協(xié)議(3)原始式套接字(RAWSOCKET)原始式套接字允許對較低層次的協(xié)議(如IP、ICMP)直接訪問,用于檢驗新協(xié)議的實現(xiàn)。原始套接字保存了數(shù)據(jù)包中的完整IP頭;前面兩種套接字只能收到用戶數(shù)據(jù);因此可以通過原始套接字對數(shù)據(jù)進(jìn)行分析。3.套接字的創(chuàng)建套接字由應(yīng)用層的通信進(jìn)程創(chuàng)建,并為其服務(wù)。即,每一個套接字都有一個相關(guān)的應(yīng)用進(jìn)程,操作該套接字的代碼是該進(jìn)程的組成部分。

4.使用確定的IP地址和傳輸層端口號在生成套接字的描述符后,要將套接字與計算機(jī)上的特定的IP地址和傳輸層端口號相關(guān)聯(lián),這個過程稱為綁定。一個套接口要使用一個確定的三元組網(wǎng)絡(luò)地址信息,才能使它在網(wǎng)絡(luò)中唯一地被標(biāo)識。2.2.3套接字的應(yīng)用場合并非所有的網(wǎng)絡(luò)應(yīng)用編程都要使用套接字。套接字適合開發(fā)具有如下特點的新網(wǎng)絡(luò)應(yīng)用。不管是采用對等模式或者客戶機(jī)/服務(wù)器模式,通信雙方的應(yīng)用程序都需要開發(fā)。雙方所交換數(shù)據(jù)的結(jié)構(gòu)和交換數(shù)據(jù)的順序有特定的要求,不符合現(xiàn)在成熟的應(yīng)用層協(xié)議,甚至需要自己去開發(fā)應(yīng)用層協(xié)議,自己設(shè)計最適合的數(shù)據(jù)結(jié)構(gòu)和信息交換規(guī)程。套接字編程層次較低,自由度較大。2.2.4套接字使用的數(shù)據(jù)類型和相關(guān)的問題1.三種表示套接字地址的結(jié)構(gòu)在套接字編程接口中,定義了三種結(jié)構(gòu)型的數(shù)據(jù)類型,用來存儲協(xié)議相關(guān)的網(wǎng)絡(luò)地址,在套接字編程接口的函數(shù)調(diào)用中要用到它們。(1)sockaddr結(jié)構(gòu):針對各種通信域的套接字,用來表示地址的一般格式,然后要求每個協(xié)議族說明其協(xié)議地址如果具體使用這個一般格式的。structsockaddr{ u_char sa_len; //地址總長 u_char sa_family; //地址家族

char sa_data[14]; //協(xié)議地址}(2)sockaddr_in結(jié)構(gòu):專門針對Internet通信域,存儲套接字相關(guān)的網(wǎng)絡(luò)地址信息,例如IP地址,傳輸層端口號等信息。structsockaddr_in{

u_char sa_len; //地址總長

u_char sin_family; //地址家族,必須設(shè)定為

u_short sin_port; //端口號,2字節(jié)

structin_addr sin_addr; //IP地址,4字節(jié)

char sin_zero[8]; //未用,全為0,8字節(jié)}

(3)in_addr結(jié)構(gòu):專門用來存儲IP地址。Structin_addr{ Unsignedlong s_addrl;}AF_INET14字節(jié)(4)這些數(shù)據(jù)結(jié)構(gòu)的一般用法首先,定義一個sockaddr_in的結(jié)構(gòu)實例,并將它清零。structsockaddr_inmyad; memset(&myad,0,sizeof(structsockaddr_in));

然后,為這個結(jié)構(gòu)賦值。

myad.sin_family=AF_INET; myad.sin_port=htons(8080); myad.sin_addr.s_addr=htonl(INADDR-ANY);第三步,在函數(shù)調(diào)用中使用時,將這個結(jié)構(gòu)強(qiáng)制轉(zhuǎn)換為sockaddr類型。

accept(listenfd,(sockaddr*)(&myad),&addrlen);2.本機(jī)字節(jié)順序和網(wǎng)絡(luò)字節(jié)順序本機(jī)字節(jié)順序:在具體計算機(jī)中的多字節(jié)數(shù)據(jù)的存儲順序。網(wǎng)絡(luò)字節(jié)順序:多字節(jié)數(shù)據(jù)在網(wǎng)絡(luò)協(xié)議報頭中的存儲順序。網(wǎng)絡(luò)應(yīng)用程序要在不同的計算機(jī)中運行,本機(jī)字節(jié)順序是不同的,但網(wǎng)絡(luò)字節(jié)順序是一定的。所以,在應(yīng)用程序編程的時候,在把IP地址和端口號裝入套接字時,應(yīng)當(dāng)把它們從本機(jī)字節(jié)順序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序;相反,在本機(jī)輸出時,應(yīng)將它們從網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換為本機(jī)字節(jié)順序。套接字編程接口為解決這個問題設(shè)置了四個函數(shù):htons()短整數(shù)本機(jī)順序轉(zhuǎn)換為網(wǎng)絡(luò)順序,用于端口號。htonl()長整數(shù)本機(jī)順序轉(zhuǎn)換為網(wǎng)絡(luò)順序,用于IP地址。ntohs()短整數(shù)網(wǎng)絡(luò)順序轉(zhuǎn)換為本機(jī)順序,用于端口號。ntohl()長整數(shù)網(wǎng)絡(luò)順序轉(zhuǎn)化為本機(jī)順序,用于IP地址。這四個函數(shù)將被轉(zhuǎn)換的數(shù)值作為函數(shù)的參數(shù),函數(shù)返回值是轉(zhuǎn)換后的結(jié)果。

3.點分十進(jìn)制的IP地址的轉(zhuǎn)換在因特網(wǎng)中,IP地址常用點分十進(jìn)制的表示方法,在套接字中,IP地址是無符號的長整型數(shù),套接字接口設(shè)置了兩個函數(shù)用于兩種形式IP地址的轉(zhuǎn)換。(1)inet-addr函數(shù)unsignedlonginet-addr(constchar*cp)入口參數(shù)cp:點分十進(jìn)制形式的IP地址。返回值:網(wǎng)絡(luò)字節(jié)順序的IP地址,是無符號的長整數(shù)。(2)inet_ntoa函數(shù)char*inet_ntoa(structin_addrin)入口參數(shù)in:包含長整型IP地址的in_addr結(jié)構(gòu)變量;返回值:指向點分十進(jìn)制IP地址的字符串的指針.4.域名服務(wù)通常,我們使用域名來標(biāo)識站點,可以將文字型的主機(jī)域名直接轉(zhuǎn)換成IP地址。structhostent*gethostbyname(constchar*name);入口參數(shù):是站點的主機(jī)域名字符串;返回值:是指向hostent結(jié)構(gòu)的指針。hostent結(jié)構(gòu)包含主機(jī)名、主機(jī)別名數(shù)組、返回地址的類型(一般是AF-INET)、地址長度的字節(jié)數(shù)、已符合網(wǎng)絡(luò)字節(jié)順序的主機(jī)網(wǎng)絡(luò)地址等。

2.3面向連接的套接字編程2.3.1套接字的工作過程面向連接的通信方式基于TCP,必須借助流式套接字來編程,應(yīng)用服務(wù)器分為服務(wù)器端和客戶端,雙方是不對稱的,需要分別編程。雙方首先要創(chuàng)建并安裝套接字,做好準(zhǔn)備;建立連接,采用三次握手的方式;數(shù)據(jù)交換,稱為客戶機(jī)與服務(wù)器的會話期,會話的內(nèi)容必須遵守一定的格式和順序;釋放連接。下面介紹面向連接的服務(wù)器和客戶端的編程步驟.2.3.2UNIX套接字編程接口的系統(tǒng)調(diào)用1.創(chuàng)建套接字SOCKET()創(chuàng)建一個套接字,并返回一個整型描述符:intSOCKET(intProtofamily,intType,intProtocol);

入口參數(shù):Protofamily:套接字使用的協(xié)議簇,一般為PF_INET,表示TCP/IP協(xié)議簇,PF_DECnet表示DEC協(xié)議。type:套接字類型,SOCK_STREMA表示創(chuàng)建面向連接的流傳輸?shù)牧魇教捉幼?;SOCK_DGRAMA表示創(chuàng)建無連接的面向消息的數(shù)據(jù)報套接字。protocol:套接字使用的傳輸層網(wǎng)絡(luò)協(xié)議,一般為IPPROTO_IP,internet通信域中一般取值為0。

返回值:套接字創(chuàng)建成功,返回一個int型的整數(shù),即所創(chuàng)建套接字的描述符,指向被維護(hù)在操作系統(tǒng)內(nèi)核里的socket數(shù)據(jù)結(jié)構(gòu)。套接字創(chuàng)建出錯,返回-1,表示出錯類型的代碼保存在全局變量Errno中。舉例:intsockfd=SOCKET(PF_INTE,SOCK_STREAM,0)創(chuàng)建套接字時,為其分配了內(nèi)存,并建立了相應(yīng)的數(shù)據(jù)結(jié)構(gòu),用于指定連接的種類和使用的協(xié)議,同時還有關(guān)于連接隊列操作的選項結(jié)構(gòu)字段,創(chuàng)建時設(shè)置為默認(rèn)值。2.綁定套接字到指定的地址BIND()intBIND(intSockfd,structsockaddr*My_addr,intAddrlen);

入口參數(shù):Sockfd:是由SOCKET()系統(tǒng)調(diào)用創(chuàng)建的套接字描述符,要將其綁定到指定的網(wǎng)絡(luò)地址上;My_addr:指向sockaddr結(jié)構(gòu)變量的指針,所指結(jié)構(gòu)中保存著特定的網(wǎng)絡(luò)地址,要將套接字綁定到這個網(wǎng)絡(luò)地址上;Addlen:是sockaddr結(jié)構(gòu)的長度。返回值:如果返回0,表示已經(jīng)綁定成功。如果返回-1,表示有錯,出錯碼在Errno中。舉例: Structsockaddr_inmy_addr; if(BIND(sockfd,(sockaddr*)&my_addr,sizeof(structsockaddr_in))<0){報錯,并退出}在服務(wù)器端,用作監(jiān)聽客戶機(jī)連接請求的套接字一定要綁定,因為大多數(shù)服務(wù)器進(jìn)程使用熟知端口,并且服務(wù)器有時有多塊網(wǎng)卡,會有多個IP地址。客戶機(jī)一般不用綁定。

返回值:如果返回0,表示已經(jīng)綁定成功。如果返回-1,表示有錯,出錯碼在Errno中。服務(wù)器調(diào)用BIND,來說明它將用來接收通信的協(xié)議端口號,同時my_addr結(jié)構(gòu)中還包含記錄IP地址的域,此時如果主機(jī)是多宿主機(jī),則in_addr字段取值常量INADDR_ANY,允許服務(wù)器在該計算機(jī)的任何IP地址上使用一個指定的端口。

3.啟動監(jiān)聽Listen()intLISTEN(intSockfd,intQueuesize);入口參數(shù):Sockfd:套接字描述符,通過它來監(jiān)聽來自客戶機(jī)端的連接請求。Queuesize:等待連接隊列的最大長度,最大可設(shè)為20,一般設(shè)為5~10。(操作系統(tǒng)為每個監(jiān)聽套接字建立一個用來等待連接的“先進(jìn)先出”緩沖區(qū)隊列,若緩沖區(qū)隊列有空就接收一個客戶端請求。)返回值:函數(shù)正確執(zhí)行則返回0,出錯則返回-1。舉例:LISTEN(Sockfe,10);本套接字只適用于面向連接的流式套接字。舉例:LISTEN(Sockfe,10);圖2.6監(jiān)聽套接字使用緩沖區(qū)接納多個客戶端的連接請求4.接收連接請求ACCEPT()intACCEPT(intSockfd,structsockaddr*Addr,int*addrlen);入口參數(shù):Sockfd:監(jiān)聽客戶端連接請求的套接字描述符。Addr:sockaddr結(jié)構(gòu)變量的指針,是一個出口參數(shù),當(dāng)調(diào)用執(zhí)行完畢時,變量中放置的是所接收客戶機(jī)端的網(wǎng)絡(luò)地址。Addrlen:整型變量指針,也是一個出口參數(shù),調(diào)用時初始設(shè)置為Addr結(jié)構(gòu)的長度,不能為0或null,調(diào)用執(zhí)行完畢時,返回所接收的客戶機(jī)端網(wǎng)絡(luò)地址長度。返回值:如果執(zhí)行正確,返回一個新的套接字描述符,這個套接字已經(jīng)與客戶機(jī)端建立了連接,并專用于此后與客戶機(jī)端交換數(shù)據(jù)。如果出錯,返回-1。說明:調(diào)用從監(jiān)聽套接字的等待隊列中,取出第一個連接請求,創(chuàng)建一個新的套接字,并通過這個新套接字向客戶機(jī)端發(fā)送連接應(yīng)答,從而與客戶機(jī)端建立連接,系統(tǒng)為這個新的套接字分配一個服務(wù)器端的自由端口號,這個套接字用于后續(xù)與客戶機(jī)端交換數(shù)據(jù),稱為響應(yīng)套接字。本調(diào)用僅適用于面向連接的流式套接字,與LISTEN()配套使用,兩個入口參數(shù)能返回客戶端的網(wǎng)絡(luò)地址.本調(diào)用以阻塞的方式工作,若監(jiān)聽套接字的等待隊列為空,則本調(diào)用就阻塞直到有連接請求的到來。舉例:intclientfd;//定義響應(yīng)套接字描述符變量intaddrlen=sizeof(sockaddr);//獲得套接字地址結(jié)構(gòu)長度。structsockaddr_incltsockaddr;//定義用于返回客戶端地址的結(jié)構(gòu)。clientfd=ACCEPT(listenfd,(sockaddr*)(&cltsockaddr),&addrlen);//接收客戶連接請求5.請求建立連接CONNECT()intCONNECT(intSockfd,structsockaddr*Service_addr,intAddrlen);入口參數(shù):Sockfd:客戶機(jī)端的請求套接字;Service_addr:存放服務(wù)器端的網(wǎng)絡(luò)地址;Addrlen:sockaddr結(jié)構(gòu)的長度。返回值:連接成功,返回0;連接未成功,返回-1。說明:用于客戶端請求連接到服務(wù)器;對于流式套接字,使用傳輸層TCP協(xié)議,服務(wù)器若接受連接請求,即把它放入監(jiān)聽套接字的緩沖區(qū)隊列,并調(diào)用ACCEPT()來接收處理;對于數(shù)據(jù)報套接字,使用傳輸層UDP協(xié)議,并不建立連接,而只是將套接字置為connected的狀態(tài),客戶機(jī)向同一服務(wù)器傳送多條信息時,意義在于使用CONNECT在套接字中記錄服務(wù)器的地址,允許客戶機(jī)只指明服務(wù)器地址一次,而不必在每條信息中都指明目的地址。舉例:if(CONNECT(sockfd,(structsockaddr*)(&serv_addr),sizeof(structsockaddr))<0){報錯,并退出}6.讀/寫套接字READ()和WRITE()intREAD(intsockfd,void*buffer,intlen)intWRITE(intsockfd,void*buffer,intlen)入口參數(shù):Sockfd:要讀/寫的套接字描述符,在客戶機(jī)端是請求套接字,在服務(wù)器端是響應(yīng)套接字;Buffer:字符串變量的指針,指向內(nèi)存中用來存放數(shù)據(jù)的讀寫緩沖區(qū),讀套接字時存放從套接字接收到的數(shù)據(jù),寫套接字的時候存放要發(fā)送的數(shù)據(jù);Len:讀寫緩沖區(qū)的長度。返回值:對于read()返回實際讀到的字符數(shù)。說明:套接字起源于UNIX,UNIX將套接字也統(tǒng)一使用read和write對I/O操作進(jìn)行數(shù)據(jù)傳輸。優(yōu)點:普遍性,應(yīng)用程序能夠創(chuàng)建一個發(fā)往或接收自一個描述符的數(shù)據(jù)傳輸,而不必知道這個描述符對應(yīng)的是一個文件還是一個套接字。缺點:套接字庫的實現(xiàn)可能對所有使用套接字的應(yīng)用增加額外的I/O開銷。7.發(fā)送SEND()和接收RECV()intSEND(intsockfd,char*buf,intlen,intflags);intRECV(intsockfd,char*buf,intlen,intflags);

入口參數(shù):sockfd:用來發(fā)送/接收數(shù)據(jù)的套接字描述符,可以是由SOCKET()創(chuàng)建的,也可以是ACCEPT()返回的;buf:用于發(fā)送/接收數(shù)據(jù)的緩沖區(qū)指針;len:要發(fā)送的或接收到的字符數(shù);flags:執(zhí)行本調(diào)用的方式,一般置為0.返回值:處理錯誤,返回-1;如果正確,對于發(fā)送返回實際發(fā)送出去的字節(jié)數(shù);對于接收返回實際讀入緩沖區(qū)的字節(jié)數(shù)。說明:和read、write一樣,必須用于已連接的套接字,無需調(diào)用者指明目的地參數(shù)。8.關(guān)閉套接字CLOSE()

intCLOSE(intsockfd);入口參數(shù):sockfd:要關(guān)閉的套接字描述符。說明:如果是面向連接的,CLOSE在關(guān)閉套接字前先終止連接;關(guān)閉一個套接字意味著立即終止對它的使用,描述符被釋放,以防止應(yīng)用程序發(fā)送或接收更多的數(shù)據(jù)。2.3.3面向連接的套接字編程實例1.實例的功能服務(wù)器對來訪的客戶計數(shù),并向客戶報告這個計數(shù)值;客戶建立與服務(wù)器的一個連接并等待它的輸出;每當(dāng)連接請求到達(dá)時,服務(wù)器生成一個可打印的ASCII串信息,將它在連接上發(fā)回,然后關(guān)閉連接;客戶顯示收到的信息,然后退出。例如,對于服務(wù)器接收的第10次客戶連接請求,該客戶將收到并打印如下信息:Thisserverhasbeencontacted10times.2.實例程序的命令行參數(shù)實例是UNIX環(huán)境下的C程序,客戶和服務(wù)器程序在編譯后,均以命令行的方式執(zhí)行。服務(wù)器程序執(zhí)行時可以帶一個命令行參數(shù),用來接收請求的監(jiān)聽套接字的協(xié)議端口號。這個參數(shù)是可選的,如果不指定端口號,代碼將使用程序內(nèi)定的缺省端口號5188。客戶程序執(zhí)行時可以帶兩個命令行參數(shù):一個是服務(wù)器所在計算機(jī)的主機(jī)名;另一個是服務(wù)器監(jiān)聽的協(xié)議端口號。這兩個參數(shù)都是可選的。如果沒有指定協(xié)議端口號,客戶使用程序內(nèi)定的缺省值5188。如果一個參數(shù)也沒有,客戶使用缺省端口和主機(jī)名localhost,localhost是映射到客戶所運行的計算機(jī)的一個別名。允許客戶與本地機(jī)上的服務(wù)器通信,對調(diào)試是很有用的。3.客戶程序代碼/*----------------------------------------------------*程序:client.c*目的:創(chuàng)建一個套接字,通過網(wǎng)絡(luò)連接一個服務(wù)器,并打印來自服務(wù)器的信息*語法:client[host[port]]*host-運行服務(wù)器的計算機(jī)的名字*port-服務(wù)器監(jiān)聽套接字所用協(xié)議端口號*注意:兩個參數(shù)都是可選的。如果未指定主機(jī)名,客戶使用localhost;如果未指定端口號,*客戶將使用PROTOPORT中給定的缺省協(xié)議端口號*----------------------------------------------------*/

#include<sys/types.h> /*基本系統(tǒng)數(shù)據(jù)類型*/#include<sys/socket.h> /*UNIX下,套接字接口包含文件*/#include<netinet/in.h> /*Internet地址簇*/#include<arpa/inet.h> /*Internet定義*/#include<netdb.h> /*網(wǎng)絡(luò)數(shù)據(jù)庫操作*/#include<stdio.h> /*標(biāo)準(zhǔn)輸入輸出*/#include<string.h>

/*字符串處理*/#definePROTOPORT5188 /*缺省協(xié)議端口號*/externinterrno;charlocalhost=“l(fā)ocalhost”;/*缺省主機(jī)名*/

main(argc,argv)intargc;char*argv[];{structhostent*ptrh;/*指向主機(jī)列表中一個條目的指針*/structsockaddr_inservaddr;/*存放服務(wù)器端網(wǎng)絡(luò)地址的結(jié)構(gòu)*/int sockfd;/*客戶端的套接字描述符*/int port;/*服務(wù)器端套接字協(xié)議端口號*/char*host;/*服務(wù)器主機(jī)名指針*/intn;/*讀取的字符數(shù)*/charbuf[1000];/*緩沖區(qū),接收服務(wù)器發(fā)來的數(shù)據(jù)*/

memset((char*)&servaddr,0,sizeof(servaddr));/*清空sockaddr結(jié)構(gòu)*/servaddr.sin_family=AF_INET;/*設(shè)置為因特網(wǎng)協(xié)議族*/

/*檢查命令行參數(shù),如果有,就抽取端口號。否則使用內(nèi)定的缺省值*/if(argc>2){port=atoi(argv[2]);/*如果指定了協(xié)議端口,就轉(zhuǎn)換成整數(shù)*/}else{port=PROTOPORT;/*否則,使用缺省端口號*/}if(port>0)/*如果端口號是合法的數(shù)值,就將它裝入網(wǎng)絡(luò)地址結(jié)構(gòu)*/

servaddr.sin_port=htons((u_short)port);else{/*否則,打印錯誤信息并退出*/fprintf(stderr,”badportnumber%s\n”,argv[2]);exit(1);}

/*檢查主機(jī)參數(shù)并指定主機(jī)名*/if(argc>1){host=argv[1];/*如果指定了主機(jī)名參數(shù),就使用它*/}else{host=localhost;/*否則,使用缺省值*/}

/*將主機(jī)名轉(zhuǎn)換成相應(yīng)的IP地址并復(fù)制到servaddr結(jié)構(gòu)中*//*從服務(wù)器主機(jī)名得到相應(yīng)的IP地址*/ptrh=gethostbyname(host);if((char*)ptrh==null){/*檢查主機(jī)名的有效性,無效則退出*/fprintf(stderr,”invalidhost:%s\n”,host);exit(1);}memcpy(&servaddr.sin_addr,ptrh->h_addr,ptrh->h_length);

Structhostent{

char*h_name;

char**h_aliases;

inth_addrtype;

inth_length;

char**h_addr_list;

};/*創(chuàng)建一個套接字*/sockfd=SOCKET(AF_INET,SOCK_STREAM,0);if(sockfd<0){fprintf(stderr,”socketcreationfailed\n”);exit(1);}/*請求連接到服務(wù)器*/if(connect(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr))<0){fprintf(stderr,”connectfailed\n”);

/*拒絕連接,報錯退出*/exit(1);}

/*從套接字反復(fù)讀數(shù)據(jù),并輸出到用戶屏幕上*/n=recv(sockfd,buf,sizeof(buf),0);while(n>0){write(1,buf,n);n=recv(sockfd,buf,sizeof(buf),0);}

/*關(guān)閉套接字*/closesocket(sockfd);

/*終止客戶程序*/exit(0);}

4.服務(wù)器實例代碼/*------------------------------------------*程序:server.c*目的:分配一個套接字,然后反復(fù)執(zhí)行如下幾步:*(1)等待客戶的下一個連接*(2)發(fā)送一個短消息給客戶*(3)關(guān)閉與客戶的連接*(4)轉(zhuǎn)向(1)步*命令行語法:server[port]*port–

服務(wù)器端監(jiān)聽套接字使用的協(xié)議端口號*注意:端口號可選。如果未指定端口號,服務(wù)器使用PROTOPORT中指定的缺省*端口號*-----------------------------------------------*/

#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<netdb.h>#include<stdio.h>#include<string.h>#definePROTOPORT5188 /*監(jiān)聽套接字的缺省協(xié)議端口號*/#define QLEN6/*監(jiān)聽套接字的請求隊列大小*/intvisits=0;/*對于客戶連接的計數(shù)*/

main(argc,argc)intargc;char*argv[];{structhostent*ptrh;/*指向主機(jī)列表中一個條目的指針*/structsockaddr_inservaddr;/*存放服務(wù)器網(wǎng)絡(luò)地址的結(jié)構(gòu)*/structsockaddr_inclientaddr;/*存放客戶網(wǎng)絡(luò)地址的結(jié)構(gòu)*/intlistenfd; /*監(jiān)聽套接字描述符*/intclientfd; /*響應(yīng)套接字描述符*/intport;/*協(xié)議端口號*/intalen;/*地址長度*/charbuf[1000];/*供服務(wù)器發(fā)送字符串所用的緩沖區(qū)*/

/*清空sockaddr結(jié)構(gòu)*/memset((char*)&servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;/*設(shè)置為因特網(wǎng)協(xié)議族*/servaddr.sin_addr.s_addr=INADDR_ANY;/*設(shè)置本地IP地址*/

/*檢查命令行參數(shù),若已指定,則使用該端口號,否則使用缺省端口號*/if(argc>1){port=atoi(argv[1]);/*如果指定了端口號,就將它轉(zhuǎn)換成整數(shù)*/}else{port=PROTOPORT;/*否則,使用缺省端口號*/}

if(port>0)/*測試端口號是否合法*/servaddr.sin_port=htons((u_short)port);else{/*打印錯誤信息并退出*/fprintf(stderr,”badportnumber%s\n”,argv[1]);exit(1);}

/*創(chuàng)建一個用于監(jiān)聽的流式套接字*/listenfd=SOCKET(AF_INET,SOCK_STREAM,0);if(listenfd<0){fprintf(stderr,“socketcreationfailed\n”);exit(1);}

/*將本地地址綁定到監(jiān)聽套接字*/if(bind(listenfd,(structsockaddr*)&servaddr,sizeof(servaddr))<0){fprintf(stderr,”bindfailed\n”);exit(1);}

/*開始監(jiān)聽,并指定監(jiān)聽套接字請求隊列的長度*/if(listen(listenfd,QLEN)<0){fprintf(stderr,”listenfiled\n”);exit(1);}

/*服務(wù)器主循環(huán)—接受和處理來自客戶端的連接請求*/while(1){alen=sizeof(clientaddr);/*接受客戶端連接請求,并生成響應(yīng)套接字*/if((clientfd=accept(listenfd,(structsockaddr*)&clientaddr,&alen))<0){fprintf(stderr,“acceptfailed\n”);exit(1);}visits++;/*累加訪問的客戶數(shù)*/sprintf(buf,“thisserverhasbeencontacted%dtime\n”,visits);send(clientfd,buf,strlen(buf),0);/*向客戶端發(fā)送信息*/closesocket(clientfd);/*關(guān)閉響應(yīng)套接字*/}}

關(guān)于阻塞的問題

圖2.7服務(wù)器進(jìn)程因調(diào)用ACCEPT()而被阻塞2.3.4進(jìn)程的阻塞問題和對策1.什么是阻塞阻塞:是指一個進(jìn)程執(zhí)行了一個函數(shù)或者系統(tǒng)調(diào)用,該函數(shù)由于某種原因不能立即完成,因而不能返回調(diào)用它的進(jìn)程,導(dǎo)致進(jìn)程受控于這個函數(shù)而處于等待的狀態(tài),進(jìn)程的這種狀態(tài)稱為阻塞。圖2.8RECV()函數(shù)的兩種執(zhí)行方式2.能引起阻塞的套接字調(diào)用在Berkeley套接字網(wǎng)絡(luò)編程接口的模型中,套接字的默認(rèn)行為是阻塞的,具體地說,在一定情況下,有多個操作套接字的系統(tǒng)調(diào)用會引起進(jìn)程阻塞。(1)ACCEPT()(2)READ()、RECV()和READFORM()(3)WRITE()、SEND()和SENDTO()(4)CONNECT()(5)SELECT()(6)CLOSESOCKET()

圖2.9采用阻塞工作模式的服務(wù)器不能很好地為多個客戶服務(wù)3.阻塞工作模式帶來的問題采用阻塞工作模式的單進(jìn)程服務(wù)器,不能很好地同時為多個客戶服務(wù)的。圖2.9是一個例子。4.一種解決方案利用UNIX操作系統(tǒng)的FORK()系統(tǒng)調(diào)用,編制多進(jìn)程并發(fā)執(zhí)行的服務(wù)器程序,可以創(chuàng)建子進(jìn)程。對每一個客戶端,用一個專門的進(jìn)程為它服務(wù),通過進(jìn)程的并發(fā)執(zhí)行,來對實現(xiàn)多個客戶的并發(fā)服務(wù).基本的編程框架是:父進(jìn)程代碼If((pid=FORK())==0){…….子進(jìn)程代碼........}elseif(pid<0){報錯信息}父進(jìn)程代碼

舉例:#include<sys/types.h>#include<sys/socket.h>#include<stdio.h>#include<arpa/inet.h>

voidmain(intargc,char**argv){intlistenfd,clientfd,pid;structsockaddr_inssockaddr,csockaddr;charbuffer[1024];intaddrlen,n;

/*創(chuàng)建監(jiān)聽套接字*/listenfd=socket(AF_INET,SOCK_STREAM,0);if(listenfd<0){fprintf(stderr,"socketerror!\n");exit(1);}

/*為監(jiān)聽套接字綁定網(wǎng)絡(luò)地址*/memset(&ssockaddr,0,sizeof(structsockaddr_in));ssockaddr.sin_family=AF_INET;ssockaddr.sin_addr.s_addr=htonl(INADDR_ANY);ssockaddr.sin_port=htons(8080);if(bind(listenfd,&ssockaddr,sizeof(structsockaddr_in))<0){fprintf(stderr,"binderror!\n");exit(2);}

/*啟動套接字的監(jiān)聽*/listen(listenfd,5);addrlen=sizeof(sockaddr);

/*服務(wù)器進(jìn)入循環(huán),接受并處理來自不同客戶端的連接請求*/while(1){clientfd=accept(listenfd,(sockaddr*)(&csockaddr),&addrlen);/*accept調(diào)用返回時,表明有客戶端請求連接,創(chuàng)建子進(jìn)程處理連接*/If((pid=FORK())==0){/*顯示客戶端的網(wǎng)絡(luò)地址*/printf("ClientAddr:%s%d\n",inet_ntoa(csockaddr.sin_addr),ntohs(csockaddr.sin_port));/*讀取客戶端發(fā)送來的數(shù)據(jù),在將它們返回到客戶端*/while((n=read(clientfd,buffer,1024))>0){buffer[n]=0;printf("ClientSend:%s",buffer);write(clientfd,buffer,n);}if(n<0){fprintf(stderr,"readerror!\n");exit(3);}

/*通信完畢,關(guān)閉與這個客戶連接的套接字*/printf("clent%sclosed!\n",inet_ntoa(csockaddr.sin_addr));close(clientfd);exit(1);}elseif(pid<0)printf("forkfailed!\n");close(clientfd);}close(listenfd);/*關(guān)閉監(jiān)聽套接字*/}

2.4無連接的套接字編程2.4.1無連接的套接字編程的兩種模式使用數(shù)據(jù)報套接字開發(fā)網(wǎng)絡(luò)應(yīng)用程序,既可以采用客戶/服務(wù)器模式,也可以采用對等模式。圖2.10對等模式的數(shù)據(jù)報套接字的編程模型1.對等模式

對等模式無連接套接字特點:應(yīng)用雙方是對等的:要經(jīng)過4個階段,創(chuàng)建套接字、綁定套接字、發(fā)送/接收數(shù)據(jù)、關(guān)閉套接字;雙方都必須確切知道對方的網(wǎng)絡(luò)地址,并在各自的進(jìn)程中將約定好的網(wǎng)絡(luò)地址綁定到自己的套接字上;每一次傳遞數(shù)據(jù)時,在sendto和recvfrom系統(tǒng)調(diào)用中必須包含對方的網(wǎng)絡(luò)地址信息;進(jìn)程也會因為發(fā)送或接收數(shù)據(jù)而發(fā)生阻塞。2.客戶/服務(wù)器模式

圖2.11C/S模式的數(shù)據(jù)報套接字的編程模型C/S模式無連接套接字特點:應(yīng)用雙方不是對等的:服務(wù)器要先啟動,被動等待訪問,要經(jīng)過創(chuàng)建套接字、綁定套接字、發(fā)送/接收數(shù)據(jù)、關(guān)閉套

溫馨提示

  • 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

提交評論