版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、精選優(yōu)質(zhì)文檔-傾情為你奉上 第十章 TCP協(xié)議,即傳輸控制協(xié)議(Transport Control Protocol),是一種面向連接的、可靠的傳輸層協(xié)議。TCP協(xié)議是為了在主機(jī)實(shí)現(xiàn)高可性包交換的傳輸協(xié)議,在計(jì)算機(jī)網(wǎng)絡(luò)中用途很廣泛。本章將通過(guò)C程序語(yǔ)言編程來(lái)實(shí)現(xiàn)一個(gè)基于TCP協(xié)議的程序,旨在向讀者介紹TCP的實(shí)現(xiàn)原理,并進(jìn)一步向讀者介紹C語(yǔ)言網(wǎng)絡(luò)編程技術(shù)。關(guān)于TCP的原理知識(shí),讀者可參見第2章。10.1 設(shè)計(jì)目的本章通過(guò)C 語(yǔ)言編程實(shí)現(xiàn)了一個(gè)TCP程序包括服務(wù)器端程序和客戶端程序,程序能實(shí)現(xiàn)基本的通信。通過(guò)本程序向讀者展示了TCP的服務(wù)器端和客戶端的操作流程,用以加深讀者對(duì)TCP原理的理解。本
2、章的部分知識(shí)點(diǎn)在前面章節(jié)也有所涉及,讀者可以由此加深印象。通過(guò)本章的學(xué)習(xí),讀者應(yīng)該對(duì)以下知識(shí)點(diǎn)有一定的了解:Winsock版本的設(shè)置、Winsock庫(kù)的加載以及Winsock錯(cuò)誤號(hào)的獲??;套接字的創(chuàng)建和關(guān)閉;TCP服務(wù)器的操作流程、客戶端的操作流程;套接字的綁定、偵聽、連接和接收操作;數(shù)據(jù)報(bào)的發(fā)送和接收;根據(jù)地址獲取主機(jī)、根據(jù)主機(jī)名獲取IP地址等信息;線程餓創(chuàng)建和參書設(shè)置;字符串比較函數(shù)的使用等。 讀者可以在本章的基礎(chǔ)上加以拓展,深刻理解TCP原理,掌握TCP編程方法和技巧,開發(fā)出自己的TCP程序。10.2 功能描述 本章用C語(yǔ)言實(shí)現(xiàn)了基于TCP的服務(wù)器端和客戶端程序,能實(shí)現(xiàn)基本的TCP通信。
3、其主要的功能包括如下。(1) 服務(wù)器端能以默認(rèn)選項(xiàng)(服務(wù)器端IP地址或主機(jī)名、端口號(hào))啟動(dòng),提供服務(wù)功能。(2) 服務(wù)器端能根據(jù)用戶指定的選項(xiàng)(服務(wù)器端IP地址或主機(jī)名、端口號(hào))啟動(dòng),提供服務(wù)和功能。(3) 服務(wù)器以錯(cuò)誤選項(xiàng)啟動(dòng)時(shí),會(huì)提示錯(cuò)誤信息,并終止程序。(4) 客戶端能連接到服務(wù)器端,發(fā)送消息到服務(wù)器端,同時(shí)也能接收來(lái)自服務(wù)器的響應(yīng)。(5) 客戶端不能連接到服務(wù)器端時(shí),能輸出錯(cuò)誤信息。(6) 客戶端以錯(cuò)誤選項(xiàng)啟動(dòng)時(shí),會(huì)提示錯(cuò)誤信息,并終止程序。10.3總體設(shè)計(jì)10.3.1 功能模塊設(shè)計(jì)1. 個(gè)功能模塊圖本程序由兩大部分組成,包括服務(wù)器端和客戶端,如圖10.1所示。服務(wù)器端包含的模塊有初始
4、模塊、循環(huán)控制模塊和服務(wù)模塊;客戶端包含的模塊有初始化模塊、功能控制模塊和數(shù)據(jù)傳輸控制模塊。1) 服務(wù)器端(1) 初始化模塊用于初始化各個(gè)全局變量賦初始值。初始化Winsock,加載Winsock庫(kù)。(2) 功能模塊控制。該模塊為其他模塊提供調(diào)用的函數(shù),包括參數(shù)獲取功能、用戶幫助功能和錯(cuò)誤輸出功能 。(3) 循環(huán)控制模塊。該模塊用于控制服務(wù)器端的服務(wù)次數(shù),如果服務(wù)次數(shù)超過(guò)指定的值則停止服務(wù)器。(4) 服務(wù)模塊。該模塊為客戶端提供服務(wù)功能,包括接收來(lái)自客戶端的數(shù)據(jù),并發(fā)送數(shù)據(jù)到客戶端。2) 客戶端(1) 初始化模塊。該模塊用于初始化客戶端的Winsock,加載Winsock庫(kù)。(2) 功能模塊控
5、制。與服務(wù)器端一樣,該模塊提供了參數(shù)獲取、用戶幫助和錯(cuò)誤輸出功能。(3) 數(shù)據(jù)傳輸控制模塊。該模塊控制著整個(gè)客戶端的數(shù)據(jù)傳輸,包括數(shù)據(jù)發(fā)送和接收等。TCP程序設(shè)計(jì) 客戶端 服務(wù)器端初始化模塊功能控制模塊數(shù)據(jù)傳輸控制模塊功能控制模塊初始化模塊服務(wù)模塊循環(huán)控制模塊 圖 10.1 功能模塊圖2. 服務(wù)器端系統(tǒng)流程圖服務(wù)器端系統(tǒng)流程圖10.2所示。程序首先調(diào)用GetArgments()函數(shù)獲取用戶提供的先項(xiàng),如果沒有提供選項(xiàng),則直接使用默認(rèn)值,如果有選項(xiàng)提供并成功獲?。ㄟx項(xiàng)錯(cuò)誤則顯示用戶幫助并終止程序),則初始化變量和Winsock,并創(chuàng)建TCP流套接字;然后解析主機(jī)名(如果選項(xiàng)提供的是IP地址 ,或
6、者使用是默認(rèn)值)或者IP地址(如果選項(xiàng)提供的是主機(jī)名),解析成功后則設(shè)置服務(wù)器地址的各個(gè)參數(shù),包括地址、IP地址和端口號(hào);接下來(lái)將創(chuàng)建的TCP流套接字和設(shè)定的服務(wù)器地址綁定(調(diào)用bing()函數(shù));綁定成功后,則開始偵聽客戶的連接,調(diào)用循環(huán)控制函數(shù)LoopControl()函數(shù)和Service()函數(shù)作接收客戶端的連接,接收數(shù)據(jù)、發(fā)送數(shù)據(jù)等操作;當(dāng)服務(wù)數(shù)達(dá)到最多的服務(wù)次數(shù)時(shí),并提示錯(cuò)誤信息(調(diào)用ErrorPrint()函數(shù)實(shí)現(xiàn))。開始獲取參數(shù)獲取成功否是初始化變量和Winsock 創(chuàng)建套接字創(chuàng)建成功解析主機(jī)名或者IP地址否是解析成功否是設(shè)置服務(wù)器地址參數(shù)綁定地址與接字綁定成功 偵聽連接否是偵聽
7、成功否是 循環(huán)控制輸出相應(yīng)錯(cuò)誤信息釋放資源關(guān)閉服務(wù)結(jié)束 圖10.2 服務(wù)器端系統(tǒng)流程圖3. 客戶端系統(tǒng)流程圖客戶端系統(tǒng)流程圖如圖10.3所示。客戶端程序執(zhí)行時(shí)必須帶選項(xiàng),程序首先判斷用戶提供的參數(shù)個(gè)數(shù),如果參數(shù)不等于3個(gè),則比表明用戶沒有提供正確的選項(xiàng),退出程序;如果參數(shù)等于3個(gè),則調(diào)用GetArgments()函數(shù)獲取用戶提高的選項(xiàng),如果獲取的選項(xiàng)錯(cuò)誤則顯示用戶幫助并終止程序,如果選項(xiàng)正確則開始創(chuàng)建TCP流套接字,成功創(chuàng)建TCP流套接字后則作和服務(wù)器類似的操作,即解析主機(jī)名或IP地址、設(shè)置服務(wù)器端地址;然后進(jìn)行連接服務(wù)器操作,若能成功連接則輸出連接信息,并發(fā)送消息到服務(wù)器端;然后接收來(lái)自服務(wù)
8、器端的響應(yīng),(消息),并將接收到的消息輸出。最后關(guān)閉套接字和釋放占用的資源。和服務(wù)器一樣,在操作過(guò)程中,任何一步操作失敗都將退出程序,并提示錯(cuò)誤信息(調(diào)用ErrorPrint()函數(shù)實(shí)現(xiàn))。開 始3個(gè)參數(shù)否是獲 取 參 數(shù)顯示用戶幫 助獲取成功否是創(chuàng) 建 套 接 字創(chuàng)建成功否是解析主機(jī)名或者IP地址解析成功否是設(shè)置服務(wù)器地址參數(shù)連 接服務(wù)器連接成功否是輸出連接信息發(fā)送 信息 到服 務(wù) 器 端接收服務(wù)器端的響應(yīng)輸出相應(yīng)錯(cuò)誤信息釋 放 資 源 關(guān)閉 套 接 字結(jié)束圖 10.3 客戶端系統(tǒng)流程圖4. 循環(huán)控制模塊(服務(wù)器端)該模塊是服務(wù)器端用于循環(huán)控制的模塊,其操作流程如圖10.4所示。當(dāng)服務(wù)器端偵
9、聽到客戶連接時(shí),調(diào)用該模塊進(jìn)行操作。首先接收客戶端的請(qǐng)求,接收成功后,根據(jù)傳入的參數(shù)is Multitasking判斷是否要?jiǎng)?chuàng)建一個(gè)線程來(lái)服務(wù)客戶端,如果is Multitasking是1則創(chuàng)建線程來(lái)服務(wù)客戶端(創(chuàng)建新線程時(shí),設(shè)置了的初始堆棧大小為1000,線程執(zhí)行函數(shù)是Service(),傳遞給Service()的參數(shù)為接收套接字),如果is Multitasking是0則直接調(diào)用 Service()函數(shù)來(lái)服務(wù)客戶端。一次服務(wù)成功后,判斷循環(huán)次數(shù)是否小于最大服務(wù)次數(shù)(可使用默認(rèn)值,也可使用參數(shù)形式提供),如果已達(dá)到最大服務(wù)次數(shù)則關(guān)閉服務(wù)器,否則繼續(xù)進(jìn)行下一次服務(wù)。開 始接收客戶端請(qǐng)求接收成功
10、?否是創(chuàng)建線程?否輸出錯(cuò)誤信息是直接調(diào)用服務(wù)函數(shù)創(chuàng)建線程設(shè)置參數(shù)和服務(wù)函數(shù)還可以服務(wù)?是否結(jié) 束圖10.4 循環(huán)控制模塊流程圖5. 服務(wù)模塊(服務(wù)器端) 服務(wù)模塊用于在服務(wù)器端為客戶端服務(wù),該模塊的實(shí)現(xiàn)較為簡(jiǎn)單,主要進(jìn)行接受和發(fā)送數(shù)據(jù)操作,其實(shí)現(xiàn)流程如圖10.5所示。首先用0初始化緩沖區(qū)response(數(shù)組),然后接收來(lái)自客戶端的數(shù)據(jù),判斷接收到的數(shù)據(jù)是否是HELLO SERVER,如果不是則表示不是對(duì)應(yīng)的客戶端,如果是則發(fā)送數(shù)據(jù)到客戶端。操作結(jié)束后關(guān)閉套接字。開 始初始 化 緩 沖區(qū)接收客戶端數(shù)據(jù)是預(yù)定義的數(shù)據(jù)?否輸出錯(cuò)誤信息是發(fā)送消息到客戶端關(guān)閉套接字結(jié) 束圖 10.5 服務(wù)模塊實(shí)現(xiàn)流程
11、圖 6. 服務(wù)模塊(服務(wù)器端) 服務(wù)模塊用于在服務(wù)器端為客戶端服務(wù),該模塊的實(shí)現(xiàn)較為簡(jiǎn)單,主要進(jìn)行接受和發(fā)送數(shù)據(jù)操作,其實(shí)現(xiàn)流程如圖10.5所示。首先用0初始化緩沖區(qū)response(數(shù)組),然后接收來(lái)自客戶端的數(shù)據(jù),判斷接收到的數(shù)據(jù)是否是HELLO SERVER,如果不是則表示不是對(duì)應(yīng)的客戶端,如果是則發(fā)送數(shù)據(jù)到客戶端。操作結(jié)束后關(guān)閉套接字。10.3.2數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)本程序沒有定義結(jié)構(gòu)體,在此僅講述服務(wù)器端和客戶端定義的全局變量。1. 服務(wù)器端 在服務(wù)器端定義了3個(gè)全局變量,分別是指向字符的指針hostName、無(wú)符號(hào)短整型變量maxService和無(wú)符號(hào)短整型port,各自表示的意義如下。c
12、har *hostName:該指針用于接收主機(jī)名選項(xiàng),可以是IP地址,也可以是主機(jī)名。Unsigned short maxServer:用于存儲(chǔ)服務(wù)器端最大的服務(wù)次數(shù),超過(guò)該次數(shù),服務(wù)器將終止服務(wù)。Unsigned short port :用于存儲(chǔ)服務(wù)器端提供的端口號(hào)。這3個(gè)變量所存儲(chǔ)的值都是表示服務(wù)器啟動(dòng)時(shí)提供的選項(xiàng),如果服務(wù)器啟動(dòng)時(shí)沒有提供這些選項(xiàng),程序?qū)凑漳J(rèn)設(shè)置的值啟動(dòng)服務(wù)器。2. 客戶端客戶端提供了和服務(wù)器端累世的兩個(gè)全局變量,氣作用和意義都是和服務(wù)器端的相同,只是這兩個(gè)變量存儲(chǔ)的值在程序中沒有默認(rèn)值,需要客戶端啟動(dòng)是提供相應(yīng)的選項(xiàng)。char* hostName:接收主機(jī)名選項(xiàng)。U
13、ndigned short port:用以存儲(chǔ)服務(wù)客戶端提供的端口號(hào)。10.3.3函數(shù)功能描述1. Initial()函數(shù)原型:void initial()Initial()函數(shù)用于初始化服務(wù)器端的全局變量,包括hostName、maxServerice和port,分別被初始化為“127.0.0.1”、“3”和“9999”。服務(wù)器在啟動(dòng)時(shí),若沒有指定這些選項(xiàng),程序?qū)⑹褂眠@些默認(rèn)值啟動(dòng)服務(wù)器。2. InitSockets()函數(shù)原型:int InitSockts(void)InitSockets()函數(shù)用于初始化Winsock。3. GetArgment()函數(shù)原型:viod GetArgmen
14、t(int argc,char*argv)GetArgment()函數(shù)用于獲取用戶提供的選項(xiàng),在服務(wù)器端能獲取的參數(shù)包括主機(jī)名(或IP地址)、最多服務(wù)次數(shù)和端口號(hào)。其中argc表示獲取的選項(xiàng)個(gè)數(shù),argv用來(lái)存儲(chǔ)獲取的選項(xiàng)值,這個(gè)參數(shù)的值通過(guò)主函數(shù)的參數(shù)傳遞過(guò)來(lái)。4. ErrorPrint()函數(shù)原型:void ErrorPoint(x)ErrorPoint()函數(shù)用于輸出錯(cuò)誤信息,該函數(shù)調(diào)用系統(tǒng)函數(shù)WSAGetLastError()來(lái)獲取錯(cuò)誤號(hào)。其中X表示錯(cuò)誤消息。5. userHelp()函數(shù)原型:void userHelp()userHelp()函數(shù)用于現(xiàn)實(shí)用戶幫戰(zhàn)。當(dāng)服務(wù)器端啟動(dòng)時(shí),若
15、提供的選項(xiàng)錯(cuò)誤,將調(diào)用該函數(shù)輸出用戶幫助信息,提供的信息包括選項(xiàng)的格式和類型。6. LoopControl()函數(shù)原型:int LoopControl(SOCKET listenfd,int isMultiTasking)LoopControl()函數(shù)用于循環(huán)控制,當(dāng)服務(wù)器的服務(wù)次數(shù)在指定的范圍內(nèi),將接收客戶端的請(qǐng)求,并創(chuàng)建一個(gè)線程(如果需要的話)來(lái)為客戶端服務(wù)(調(diào)用Service()函數(shù))。其中l(wèi)istenfd表示偵聽套接字,isMultiTasking是個(gè)標(biāo)記,如果其設(shè)置為1,則創(chuàng)建一個(gè)線程來(lái)服務(wù)器端,如果其設(shè)置為0,則直接調(diào)用服務(wù)器函數(shù)來(lái)服務(wù)客戶端。7. Service()函數(shù)原型:vo
16、id Service(LPVOID lpv)Service()函數(shù)用于服務(wù)客戶端,包括接收客戶端的數(shù)據(jù)和發(fā)送數(shù)據(jù)到客戶端。2. 客戶端客戶端的這幾個(gè)函數(shù)在服務(wù)器端也出現(xiàn)過(guò),其功能和服務(wù)器端的函數(shù)類似。1 InitSockets()函數(shù)原型:int InitSockets(void)InitSockets()函數(shù)用于初始化Winsock。2 GetArgument()函數(shù)原型:void GetArgument(int argc , char *argv)GetArguemnt()函數(shù)用于獲取用戶提供的選項(xiàng),在客戶端能獲取的參數(shù)包括主機(jī)名(或IP地址)和端口號(hào)。其中argc和argv值也是通過(guò)主函
17、數(shù)的參數(shù)傳遞過(guò)來(lái),其表示的意義和主函數(shù)中的一樣。3 ErrorPrint()函數(shù)原型:void Errorprint()ErrorPrint()函數(shù)用于輸出錯(cuò)誤信息。4 userHelp()函數(shù)原型:void userHelp()userHelp()函數(shù)用于顯示用戶幫助。當(dāng)客戶端不帶選項(xiàng)啟動(dòng)時(shí)或帶錯(cuò)誤選項(xiàng)啟動(dòng)時(shí)將調(diào)用該函數(shù)顯示用戶幫助,顯示選項(xiàng)的格式和類型。10.4程序?qū)崿F(xiàn)10.4.1 源碼分析1 服務(wù)端(service.c)1 程序預(yù)處理程序處理包括庫(kù)文件的導(dǎo)入、頭文件的加載以及常量和全局變量的定義 /*導(dǎo)入庫(kù)文件*/#pragma comment(lib,wsock32.lib)/*加載頭
18、文件*/#include #include /*自定義函數(shù)原型*/void initial();int InitSockets(void);void GetArgments(int argc, char *argv);void ErrorPrint(x);void userHelp();int LoopControl(SOCKET listenfd, int isMultiTasking);void Service(LPVOID lpv);/*定義常量*/#define MAX_SER 10/*定義全局變量*/char *hostName;unsigned short maxService;u
19、nsigned short port;2初始化模塊初始化模塊由兩部分組成,包括全局變量的初始化和Winsock的初始化,由兩個(gè)函數(shù)來(lái)實(shí)現(xiàn)1 void initial(),初始化全局變量,其中hostName被賦值為“127.0.0.1”,表明程序在運(yùn)行是僅限制客戶端和服務(wù)器端在同一主機(jī)上進(jìn)行,如果要改變?cè)撝?,需要在服?wù)器啟動(dòng)是,設(shè)置hostName選項(xiàng),重新賦值;maxService表示最大的服務(wù)次數(shù),其值應(yīng)該不大于MAX SER代表的值。2 int InitSockets(void),初始化Winsock,包括初始化套接字版本號(hào)和加載Winsockku。/*初始化全局變量函數(shù)*/void i
20、nitial() hostName = 127.0.0.1; maxService = 3; port = 9999;/*初始化Winsocket函數(shù)*/int InitSockets(void) WSADATA wsaData; WORD sockVersion; int err; /*設(shè)置Winsock版本號(hào)*/ sockVersion = MAKEWORD( 2, 2 ); /*初始化Winsock*/ err = WSAStartup( sockVersion, &wsaData ); /*如果初始化失敗*/ if ( err != 0 ) printf(Error %d: Winso
21、ck not availablen, err); return 1; return 03. 功能控制模塊功能控制模塊提供了參數(shù)獲取功能、錯(cuò)誤輸出功能和用戶幫助功能,這幾個(gè)功能分別由GetArgments(int argc ,char *argv),獲取用戶提供的選項(xiàng)值。該函數(shù)首先判斷每個(gè)參數(shù)的第一個(gè)字符,如果第一個(gè)字符是“-”(短橫線)則表示該參數(shù)是用戶提供的選項(xiàng)。提供的選項(xiàng)包括“-p(-p)”,表示端口號(hào);“-h(-H)”,表示主機(jī)名(或者ip地址);“-n(-N)”,表示服務(wù)器端的最多服務(wù)次數(shù),超過(guò)該服務(wù)次數(shù)服務(wù)器將自動(dòng)停止。(2) void ErrorPrint(x),錯(cuò)誤輸出函數(shù)。(3
22、) void userHelp(),顯示用戶幫助函數(shù)。在GetArgments()函數(shù)中,如果獲取的選項(xiàng)值不是預(yù)定義的值,則調(diào)用該函數(shù)輸出用戶幫助。/*獲取選項(xiàng)函數(shù)*/void GetArgments(int argc, char *argv) int i; for(i=1; i 3) port = atoi(&argvi3); break; /*若是主機(jī)名*/ case h: hostName = &argvi3; break; /*最多服務(wù)次數(shù)*/ case n: maxService = atoi(&argvi3); break; /*其他情況*/ default: userHelp()
23、; break; return;/*錯(cuò)誤輸出函數(shù)*/void ErrorPrint(x) printf(Error %d: %sn, WSAGetLastError(), x);/*用戶幫助函數(shù)*/void userHelp() printf(userHelp: -h:str -p:int -n:intn); printf( -h:str The host name n); printf( The default host is 127.0.0.1n); printf( -p:int The Port number to usen); printf( The default port is 9
24、999n); printf( -n:int The number of service,below MAX_SER n); printf( The default number is 3n); ExitProcess(-1);4) 循環(huán)控制模塊 循環(huán)控制模塊的功能是由LoopControl()函數(shù)實(shí)現(xiàn)的。具體步驟可參見10.3.3節(jié)中的函數(shù)功能描述其操作流程圖可參見圖10.4。/*循環(huán)控制函數(shù)*/int LoopControl(SOCKET listenfd, int isMultiTasking) SOCKET acceptfd; struct sockaddr_in clientAddr;
25、 int err; int nSize; int serverNum = 0; HANDLE handlesMAX_SER; int myID; /*服務(wù)次數(shù)小于最大服務(wù)次數(shù)*/ while (serverNum maxService) nSize = sizeof(clientAddr); /*接收客戶端請(qǐng)求*/ acceptfd = accept(listenfd, (struct sockaddr *) &clientAddr, &nSize); /*如果接收失敗*/ if (acceptfd = INVALID_SOCKET) ErrorPrint(Error: accept fail
26、edn); return 1; /*接收成功*/ printf(Accepted connection from client at %sn, inet_ntoa(clientAddr.sin_addr); /*如果允許多任務(wù)執(zhí)行*/ if (isMultiTasking) /*創(chuàng)建一個(gè)新線程來(lái)執(zhí)行任務(wù),新線程的初始堆棧大小為1000,線程執(zhí)行函數(shù) 是Service(),傳遞給Service()的參數(shù)為acceptfd*/ handlesserverNum = CreateThread(NULL, 1000, (LPTHREAD_START_ROUTINE)Service, (LPVOID)
27、acceptfd, 0, &myID); else /*直接調(diào)用服務(wù)客戶端的函數(shù)*/ Service(LPVOID) acceptfd); serverNum+; if (isMultiTasking) /*在一個(gè)線程中等待多個(gè)事件,當(dāng)所有對(duì)象都被通知時(shí)函數(shù)才會(huì)返回,并且等待沒有時(shí)間限制*/ err = WaitForMultipleObjects(maxService, handles, TRUE, INFINITE); printf(Last thread to finish was thread #%dn, err); return 0;5) 服務(wù)模塊 服務(wù)模塊的功能由函數(shù)Service
28、()來(lái)實(shí)現(xiàn)。其功能主要是接收、判斷來(lái)自客戶端的數(shù)據(jù),以及發(fā)送數(shù)據(jù)到客戶端。Service()函數(shù)首先接收客戶端發(fā)送來(lái)的數(shù)據(jù),存放到緩沖區(qū)response中,然后判斷接收到的數(shù)據(jù)是否和預(yù)定義的數(shù)據(jù)“HELLO SERVER”相同,如果相同則發(fā)送消息到客戶端,并關(guān)閉套接字;否則,輸出錯(cuò)誤信息并關(guān)閉套接字。其實(shí)現(xiàn)流程圖可參見圖10.5。/*服務(wù)函數(shù)*/void Service(LPVOID lpv) SOCKET acceptfd = (SOCKET) lpv; const char *msg = HELLO CLIENT; char response4096; /*用0初始化response409
29、6數(shù)組*/ memset(response, 0, sizeof(response); /*接收數(shù)據(jù),存入response中*/ recv(acceptfd, response, sizeof(response), 0); /*如果接收到的數(shù)據(jù)和預(yù)定義的數(shù)據(jù)不同*/ if (strcmp(response, HELLO SERVER) printf(Application: client not using expected protocol %sn, response); else /*發(fā)送服務(wù)器端信息到客戶端*/ send (acceptfd, msg, strlen(msg)+1, 0)
30、; /*關(guān)閉套接字*/ closesocket(acceptfd);6) 主函數(shù) 主函數(shù)控制著整個(gè)程序的流程,包括套接字的創(chuàng)建、綁定、偵聽和釋放,以及對(duì)各個(gè)模塊中函數(shù)的調(diào)用等。其具體操作流程圖可參見圖10.2。/*主函數(shù)*/int main(int argc, char *argv) SOCKET listenfd; int err; struct sockaddr_in serverAddr; struct hostent *ptrHost; initial(); GetArgments(argc,argv); InitSockets(); /*創(chuàng)建TCP流套接字,在domain參數(shù)為PF_
31、INET的SOCK_STREAM套接口中,protocol參數(shù)為0意味 著告訴內(nèi)核選擇 IPPRPTP_TCP,這也意味著套接口將使用TCP/IP協(xié)議*/ listenfd = socket(PF_INET, SOCK_STREAM, 0); /*如果創(chuàng)建套接字失敗*/ if (listenfd = INVALID_SOCKET) printf(Error: out of socket resourcesn); return 1; /*如果是IP地址*/ if (atoi(hostName) /*將IP地址轉(zhuǎn)換成32二進(jìn)制表示法,返回32位二進(jìn)制的網(wǎng)絡(luò)字節(jié)序*/ u_long ip_addr
32、= inet_addr(hostName); /*根據(jù)IP地址找到與之匹配的主機(jī)名*/ ptrHost = gethostbyaddr(char *)&ip_addr, sizeof(u_long), AF_INET); /*如果是主機(jī)名*/ else /*根據(jù)主機(jī)名獲取一個(gè)指向hosten的指針,該結(jié)構(gòu)中包含了該主機(jī)所有的IP地址*/ ptrHost = gethostbyname(hostName); /*如果解析失敗*/ if (!ptrHost) ErrorPrint(cannot resolve hostname); return 1; /*設(shè)置服務(wù)器地址*/ /*設(shè)置地址族為PF_
33、INET*/ serverAddr.sin_family = PF_INET; /*將一個(gè)通配的Internet地址轉(zhuǎn)換成無(wú)符號(hào)長(zhǎng)整型的網(wǎng)絡(luò)字節(jié)序數(shù)*/ serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); /*將端口號(hào)轉(zhuǎn)換成無(wú)符號(hào)短整型的網(wǎng)絡(luò)字節(jié)序數(shù)*/ serverAddr.sin_port = htons(port); /*將套接字與服務(wù)器地址綁定*/ err = bind(listenfd, (const struct sockaddr *) &serverAddr, sizeof(serverAddr); /*如果綁定失敗*/ if (err
34、 = INVALID_SOCKET) ErrorPrint(Error: unable to bind socketn); return 1; /*開始偵聽,設(shè)置等待連接的最大隊(duì)列長(zhǎng)度為SOMAXCONN,默認(rèn)值為5個(gè)*/ err = listen(listenfd, SOMAXCONN); /*如果偵聽失敗*/ if (err = INVALID_SOCKET) ErrorPrint(Error: listen failedn); return 1; LoopControl(listenfd, 1); printf(Server is downn); /*釋放Winscoket初始化時(shí)占用的
35、資源*/ WSACleanup(); return 0; 117.73.130.1622. 客戶端(client.c)1) 程序預(yù)處理 與服務(wù)器一樣,客戶端的預(yù)處理也包括庫(kù)文件的導(dǎo)入、頭文件的加載和全局變量的定義。/*導(dǎo)入庫(kù)文件*/#pragma comment(lib,wsock32.lib)/*加載頭文件*/#include #include /*自定義函數(shù)*/int InitSockets(void);void GetArgument(int argc, char *argv);void ErrorPrint(x);void userHelp();/*定義全局變量*/unsigned s
36、hort port;char *hostName;2) 初始化模塊 因?yàn)椴淮嬖趯?duì)全局變量賦初始值,所以客戶端的初始化模塊僅僅初始化Winsock,包括初始化套接字版本號(hào)加載Winsock庫(kù)。/*初始化Winsock函數(shù)*/int InitSockets(void) WSADATA wsaData; WORD sockVersion; int err;/*設(shè)置Winsock版本號(hào)*/ sockVersion = MAKEWORD( 2, 2 ); /*初始化Winsock*/ err = WSAStartup( sockVersion, &wsaData ); /*如果初始化失敗*/ if (
37、err != 0 ) printf(Error %d: Winsock not availablen, err); return 1; return 0;3) 功能控制模塊 功能控制模塊包括選項(xiàng)獲取功能、錯(cuò)誤輸出功能和用戶幫助功能。這幾個(gè)功能分別由GetArgment()函數(shù)、ErrorPrint()函數(shù)和userHelp()函數(shù)來(lái)實(shí)現(xiàn),這幾個(gè)函數(shù)和服務(wù)器端的函數(shù)功能、參數(shù)意義相同,在此就不再贅述。(1) void GetArgment(int argc,char *argv),獲取用戶提供的選項(xiàng)。(2) void ErrorPrint(x),輸出錯(cuò)誤信息。(3) void userHelp(
38、),顯示用戶幫助。/*獲取選項(xiàng)函數(shù)*/void GetArgments(int argc, char *argv) int i; for(i=1; i 3) port = atoi(&argvi3); break; /*若是主機(jī)名*/ case h: hostName = &argvi3; break; /*其他情況*/ default: userHelp(); break; return;/*錯(cuò)誤輸出函數(shù)*/void ErrorPrint(x) printf(Error %d: %sn, WSAGetLastError(), x);/*用戶幫助函數(shù)*/void userHelp() prin
39、tf(userHelp: -h:str -p:intn); printf( -h:str The host name n); printf( -p:int The Port number to usen); ExitProcess(-1);4) 數(shù)據(jù)傳輸控制模塊 客戶端程序把數(shù)據(jù)的輸入輸出部分都放在主函數(shù)中執(zhí)行,即數(shù)據(jù)傳輸控制由主函數(shù)來(lái)實(shí)現(xiàn)。主函數(shù)中包括套接字的創(chuàng)建、綁定和釋放,服務(wù)器的連接,數(shù)據(jù)的發(fā)送、接收以及對(duì)各個(gè)模塊中函數(shù)的調(diào)用等。其具體操作流程圖可參見圖10.3。/*主函數(shù)*/int main(int argc, char *argv) SOCKET clientfd; int err
40、; struct sockaddr_in serverAddr; struct hostent *ptrHost; char response4096; char *msg = HELLO SERVER;GetArgments(argc, argv); if (argc != 3) userHelp(); return 1; GetArgments(argc,argv); InitSockets();/*創(chuàng)建套接字*/ clientfd = socket(PF_INET, SOCK_STREAM, 0);/*如果創(chuàng)建失敗*/ if (clientfd = INVALID_SOCKET) Err
41、orPrint(no more socket resources);return 1;/*根據(jù)IP地址解析主機(jī)名*/ if (atoi(hostName) u_long ip_addr = inet_addr(hostName); ptrHost = gethostbyaddr(char *)&ip_addr, sizeof(u_long), AF_INET); /*根據(jù)主機(jī)名解析IP地址*/ else ptrHost = gethostbyname(hostName);/*如果解析失敗*/ if (!ptrHost) ErrorPrint(cannot resolve hostname);r
42、eturn 1; /*設(shè)置服務(wù)器端地址選項(xiàng)*/serverAddr.sin_family = PF_INET; memcpy(char *) &(serverAddr.sin_addr), ptrHost-h_addr,ptrHost-h_length); serverAddr.sin_port = htons(port);/*連接服務(wù)器*/ err = connect(clientfd, (struct sockaddr *) &serverAddr, sizeof(serverAddr);/*連接失敗*/ if (err = INVALID_SOCKET)ErrorPrint(cannot
43、 connect to server);return 1;/*連接成功后,輸出信息*/ printf(You are connected to the servern);/*發(fā)送消息到服務(wù)器端*/ send (clientfd, msg, strlen(msg)+1, 0); memset(response, 0, sizeof(response);/*接收來(lái)自服務(wù)器端的消息*/ recv(clientfd, response, sizeof(response), 0); printf(server says %sn, response);/*關(guān)閉套接字*/ closesocket(clien
44、tfd);/*釋放Winscoket初始化時(shí)占用的資源*/ WSACleanup(); return 0; 提示: 由于在TC或者Win-TC中沒有編譯套接字的頭文件,所以該程序需要在Visual C+或者具有Winsock頭文件的編譯器中編譯。本章的服務(wù)器端和客戶端程序端都已經(jīng)在Visual C+6.0中通過(guò)編譯。10.4.2 運(yùn)行結(jié)果 本節(jié)將對(duì)服務(wù)器端和客戶端從兩個(gè)大方面進(jìn)行測(cè)試,包括錯(cuò)誤測(cè)試和帶選項(xiàng)(帶正確選項(xiàng)值)的測(cè)試。1 錯(cuò)誤測(cè)試由于服務(wù)器端可以不帶選項(xiàng)進(jìn)行啟動(dòng),所以對(duì)服務(wù)器端的錯(cuò)誤測(cè)試主要是帶錯(cuò)誤選項(xiàng)的測(cè)試;而客戶端的錯(cuò)誤測(cè)試包括不帶選項(xiàng)啟動(dòng)、帶不正確的端口號(hào)或者主機(jī)名啟動(dòng),以及服務(wù)器未啟動(dòng)時(shí)啟動(dòng)客戶端。1) 服務(wù)器端選項(xiàng)錯(cuò)誤 如圖10。6所示,服務(wù)器端錯(cuò)誤選項(xiàng)(“-1“)啟動(dòng)時(shí),則會(huì)顯示用戶幫助信息(選項(xiàng)格式和類型),并終止程序。 C:WINDOWSsystem32cmd.exeE:bookstrchapt10tcpDebugtcp.
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2024-2030年電腦泡沫清潔劑搬遷改造項(xiàng)目可行性研究報(bào)告
- 2024-2030年版中國(guó)果品批發(fā)市場(chǎng)競(jìng)爭(zhēng)策略及投資產(chǎn)銷狀況分析報(bào)告
- 2024-2030年版中國(guó)嬰幼兒服裝行業(yè)市場(chǎng)銷售模式及發(fā)展前景展望報(bào)告
- 2024-2030年新版中國(guó)稀土合金材料項(xiàng)目可行性研究報(bào)告(甲級(jí)資質(zhì))
- 2024-2030年新版中國(guó)液化石油氣鋼瓶項(xiàng)目可行性研究報(bào)告
- 2024-2030年新版中國(guó)塑鋼爬梯項(xiàng)目可行性研究報(bào)告
- 2024-2030年新版中國(guó)中控百葉簾項(xiàng)目可行性研究報(bào)告
- 2024-2030年帶式定量給料機(jī)搬遷改造項(xiàng)目可行性研究報(bào)告
- 2024-2030年商務(wù)酒店產(chǎn)業(yè)市場(chǎng)深度分析及前景趨勢(shì)與投資研究報(bào)告
- 2024-2030年冶金橋式起重機(jī)搬遷改造項(xiàng)目可行性研究報(bào)告
- 醫(yī)院卒中中心建設(shè)各種制度、流程匯編
- 郵儲(chǔ)高級(jí)練習(xí)卷三(第12章-第17章)附有答案
- 重慶市江北區(qū)2023-2024學(xué)年六年級(jí)下學(xué)期期末考試數(shù)學(xué)試題
- 軍隊(duì)文職聘用合同管理規(guī)定
- 2024年貴州省安順市西秀區(qū)小升初語(yǔ)文試卷
- 2024-2029年中國(guó)兒童牙冠行業(yè)市場(chǎng)現(xiàn)狀分析及競(jìng)爭(zhēng)格局與投資發(fā)展研究報(bào)告
- 新時(shí)代鐵路發(fā)展面對(duì)面全文內(nèi)容
- 人工智能與語(yǔ)文閱讀理解教學(xué)
- 科學(xué)素養(yǎng)培育及提升-知到答案、智慧樹答案
- 快遞主管崗位職責(zé)
- 醫(yī)療差錯(cuò)、糾紛、事故登記表
評(píng)論
0/150
提交評(píng)論