WinSocket編程_第1頁
WinSocket編程_第2頁
WinSocket編程_第3頁
WinSocket編程_第4頁
WinSocket編程_第5頁
已閱讀5頁,還剩23頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

1、第 頁共26頁網(wǎng)絡(luò)socket編程指南sockets(套接字)編程有三種,流式套接字(SOCK.STREAM),數(shù)據(jù)報套接字(SOCK.DGRAM),原始套接字(SOCK_RAW):基于TCP的socket編程是采用的流式套接字。在這個程序中,將兩個工程添加到一個工作區(qū)。要璉接一個ws2_32.1ib的庫文件。服務(wù)器端編程的步驟:1:加栽套接字庫,創(chuàng)建套接字(WSAStartupO/socketO):2:綁定套接字到一個IP地址和一個端口上(bind();3:將套接字設(shè)置為監(jiān)聽模式等待連接請求(listenO):4:請求到來后,接受連接請求,返凹一個新的對應(yīng)于此次連接的套接字(acceptO)

2、:5:用返回的套接字和客戶端進行通信(send()/recv():6:返回,等待窮一連接請求:7:關(guān)閉套接字,關(guān)閉加載的套接字庫(closesocket()AVSACleanup()o服務(wù)器端代碼如下:#include#includevoidmain()WORDwVersionRequested;WSADATAwsaData;interr;wVersionRequested=MAKEWORD(1.1);err=WSAStartup(wVersionRequested.&wsaData);if(err!=0)return:Iif(LOBYTE(wsaData.wVersion)!=1IIHIBY

3、TE(wsaData.wVersion)!=1)WSACleanup();return:SOCKETsockSrv=socket(AF_INET.SOCK_STREAM.O);SOCKADDR.INaddrSrv;addrSrvr.sin_addr.S_un.S_addr=hlonl(INADDR_ANY):addrSrv.sin_family=AF_INET;addrSrvr.sin_port=htons(6000);bind(sockSrv.(SOCKADDR*)&addrSrv.sizeof(SOCKADDR);listen(sockSrv.5);SOCKADDR_INaddrClien

4、t;intlen=sizeof(SOCKADDR);while(l)SOCKETsockConn=accept(sockSrv(SOCKADDR*)&addrlient,&len);charsendBufI50:sprintf(sendBuf.Welcome%stohere!inet_ntoa(addrlient.sin_addr);send(sockConn,sendBuf,strlen(sendBuf)+1,0);charrecvBuf50;recv(sockConn.recvBuf,50.0);prinlf(”sn.recvBuf);closesocket(sockConn);II客戶端

5、編程的步驟:1:加栽套接字庫,創(chuàng)建套接字(WSAStartupO/socketO):2:向服務(wù)益發(fā)出連接請求(connectO);3:和服務(wù)益端進行通信(sendO/recvO);4:關(guān)閉套接字,關(guān)閉加載的套接字庫(closesocket()AVSACleanup()o客戶端的代碼如下:#include#includevoidmain()WORDwVersionRequested;WSADATAwsaData;interr;Sck0072010-1-13第 頁共26頁wVersionRequested=MAKEWORD(LI);err=WSAStartup(wVersionRequested.

6、&wsaData);if(err!=0)return:Iif(LOBYTE(wsaData.wVersion)!=1IIHIBYTE(wsaData.wVersion)!=1)WSACleanup();return:ISOCKETsockClient=socket(AF_INET.SOCK_STREAM.O);SOCKADDR_INaddrSrv-addrSrvr.sin_addr.S_un.S_addr=inet_addr(,*M);addrSrv.sin_family=AF_INET;addrSrvr.sin_port=htons(6000);connect(sockClient,(SOC

7、KADDR*)&addrSrv.sizeof(SOCKADDR);charrecvBuf50J;recv(sockClient.recvBuf.50,0):printf(”snrecBuf):send(sockClienLMhelloH.strlen(,heno,)+LO);closesocket(sockClient);WSACleanup();I最初Window推出的Socketl.1只應(yīng)用丁當時流行的TCP/IP,不支持其它的傳輸協(xié)議。用Socketl.1實現(xiàn)的程序只支持兩種類型的Socket,即面向連接的SOCK_STREAM類型和面向無連接的SOCK_DGRAMo鑒于1.1版本的局限

8、性,微軟乂開發(fā)了Socket2.0o其中一個主要目的是想提供一個與協(xié)議無關(guān)的接口,全而滿足多媒體實時通信的需耍。這里注意到,Socket2.0并不是一種協(xié)議,而是一種接口,它能發(fā)現(xiàn)多種可用的傳輸協(xié)議。因此,在Socket2.0中加入了一些新函數(shù),這些函數(shù)都是以WSA打頭的。Socket2.0改變了它的體系結(jié)構(gòu),以便更容易訪問多種傳輸協(xié)議。按照Windows的開放系統(tǒng)體系結(jié)構(gòu)模型(W0SA)的原則,Socket2.0定義出了標準的服務(wù)提供商接口(ServiceProviderInterface,SPI)。這些接口介丁應(yīng)用程序接口(ApplicationProgiammingInterface,A

9、PI)及協(xié)議棧(ProtocolStacks)之間,是給不同的服務(wù)提供商用的。程序中用的是API。如錯誤!未找到引用源。所示Beej網(wǎng)絡(luò)socket編程指南介紹Socket編程讓你沮喪嗎?從manpages中很難得到有用的信息嗎?你想跟上時代去編Internet相關(guān)的程序,但是為你在調(diào)用connect()前的bind()的結(jié)構(gòu)而不知所措?等等好在我已經(jīng)將這些事完成了,我將和所有人共享我的知識了。如果你了解C語言并想穿過網(wǎng)絡(luò)編程的沼澤,那么你來對地方了。讀者對彖這個文檔是一個指南,而不是參考書。如果你剛開始socket編程并想找一本入門書,那么你是我的讀者。但這不是一本完全的socket編程書。

10、平臺和編譯器這篇文檔中的人多數(shù)代碼都在Linux平臺PC上用GNU的gcc成功編譯過。而且它們在HPUX平臺上用gcc也成功編譯過。但是注意,并不是每個代碼片段都獨立測試過。目錄:什么是套接字?Internet套接字的兩種類型網(wǎng)絡(luò)理論結(jié)構(gòu)體本機轉(zhuǎn)換IP地址和如何處理它們socket()函數(shù)bind()函數(shù)connect()函數(shù)listen()函數(shù)accept。函數(shù)send()和recv()函數(shù)sendto()和recvfrom()函數(shù)close()和shutdown()函數(shù)getpeernanie()函數(shù)gethostname()函數(shù)域名服務(wù)(DNS)客戶-服務(wù)器背景知識簡單的服務(wù)器簡單的客戶

11、端數(shù)據(jù)報套接字Socket阻塞select()-多路同步I/O參考資料什么是socket?你經(jīng)常聽到人們談?wù)撝皊ocket”,或許你還不知道它的確切含義?,F(xiàn)在讓我告訴你:它是使用標準Unix文件描述符(filedescriptor)和其它程序通訊的方式。什么?你也許聽到一些Unix高手(hacker)這樣說過:呀,Unix中的一切就是文件!”那個家伙也許正在說到一個事實:Unix程序在執(zhí)行任何形式的I/O的時候,程序是在讀或者寫一個文件描述符。一個文件描述符只是一個和打開的文件相關(guān)聯(lián)的整數(shù)。但是(注意后面的話),這個文件可能是一個網(wǎng)絡(luò)連接,F(xiàn)IFO,管道,終端,磁盤上的文件或者什么其它的東西

12、。Unix中所有的東西就是文件!所以,你想和Internet別的程序通訊的時候,你將要使用到文件描述符。你必須理解剛才的話?,F(xiàn)在你腦海中或許冒出這樣的念頭:“那么我從哪里得到網(wǎng)絡(luò)通訊的文件描述符呢?”,這個問題無論如何我都要回答:你利用系統(tǒng)調(diào)用socket(),它返回套接字描述符(socketdescriptor),然后你再通過它來進行send()和recv()調(diào)用。但是你可能有很人的疑惑,“如果它是個文件描述符,那么為什么不用一般調(diào)用read()和write()來進行套接字通訊?”簡單的答案是:“你可以使用!”。詳細的答案是:“你可以,但是使用send()和recv()讓你更好的控制數(shù)據(jù)傳輸

13、?!贝嬖谶@樣一個情況:在我們的世界上,有很多種套接字。DARPAInternet地址(Internet套接字),本地節(jié)點的路徑名(Unix套接字),CCITTX.25地址(你可以將X.25套接字完全忽略)。也許在你的Unix機器上還有其它的。我們在這里只講第一種:Internet套接字。Internet套接字的兩種類型什么意思?有兩種類型的Internet套接字?是的。不,我在撒謊。其實還有很多,但是我可不想嚇著你。我們這里只講兩種。除了這些,我打算另外介紹的RawSockets也是非常強人的,很值得查閱。那么這兩種類型是什么呢?一種是StreamSockets(流格式),另外一種是Datag

14、ramSockets1(數(shù)據(jù)包格式)。我們以后談到它們的時候也會用到“SOCK_STREAM“和SOCK_DGRAM數(shù)據(jù)報套接字有時也叫“無連接套接字”(如果你確實要連接的時候可以用connect()o)流式套接字是可靠的雙向通訊的數(shù)據(jù)流。如果你向套接字按順序輸出“1,2”,那么它們將按順序“1,2”到達另一邊。它們是無錯誤的傳遞的,有自己的錯誤控制,在此不討論。有什么在使用流式套接字?你可能聽說過telnet,不是嗎?它就使用流式套接字。你需要你所輸入的字符按順序到達,不是嗎?同樣,WWW瀏覽器使用的HTTP協(xié)議也使用它們來卜載頁而。實際上,當你通過端II80telnet到一個WWW站點,然

15、后輸入“GETpagename的時候,傷也可以得到HTML的內(nèi)容。為什么流式套接字可以達到高質(zhì)駅的數(shù)據(jù)傳輸?這是因為它使用了傳輸控制協(xié)議(TheTransmissionControlProtocol),也叫“TCP”(請參考RFC-793獲得詳細資料。)TCP控制你的數(shù)據(jù)按順序到達并且沒何錯誤。你也許聽到“TCP”是因為聽到過“TCP/IP”。這里的IP是指Internet協(xié)議”(請參考RFC-791。)IP只是處理Internet路由而已。那么數(shù)據(jù)報套接字呢?為什么它叫無連接呢?為什么它是不可靠的呢?有這樣的一些事實:如果你發(fā)送一個數(shù)據(jù)報,它可能會到達,它可能次序顛倒了。如果它到達,那么在這

16、個包的內(nèi)部是無錯誤的。數(shù)據(jù)報也使用IP作路由,但是它不使用TCP。它使用“用戶數(shù)據(jù)報協(xié)議(UserDatagramProtocol),也叫“UDP”(請參考RFC-768。)為什么它們是無連接的呢?主要是因為它并不彖流式套接字那樣維持一個連接。你只要建立一個包,構(gòu)造一個有目標信息的IP頭,然后發(fā)出去。無需連接。它們通常使用于傳輸包-包信息。簡單的應(yīng)用程序t HYPERLINK ftp:/ftp.bootpftp.bootp你也許會想:“假如數(shù)據(jù)丟失了這些程序如何正常工作?”我的朋友,每個程序在UDP上有自己的協(xié)議。例如,(ftp協(xié)議每發(fā)出的一個被接受到包,收到者必須發(fā)回一個包來說“我收到了!”

17、(一個“命令正確應(yīng)答”也叫“ACK”包)。如果在一定時間內(nèi)(例如5秒),發(fā)送方?jīng)]有收到應(yīng)答,它將重新發(fā)送,直到得到ACK。這一ACK過程在實現(xiàn)SOCK_DGRAM應(yīng)用程序的時候非常重要。網(wǎng)絡(luò)理論既然我剛才提到了協(xié)議層,那么現(xiàn)在是討論網(wǎng)絡(luò)究竟如何工作和一些關(guān)于SOCK_DGRAM包是如何建立的例子。當然,你也可以跳過這一段,如果你認為已經(jīng)熟悉的話?,F(xiàn)在是學習數(shù)據(jù)封裝(DataEncapsulation)的時候了!它非常非常重要。它重要性重要到你在網(wǎng)絡(luò)課程學(圖1:數(shù)據(jù)封裝)習中無論如何也得也得掌握它。主要的內(nèi)容是:一個包,先是被第一個協(xié)議(在這里是TFTP)在它的報頭(也許是報尾)包裝(“封裝”

18、),然后,整個數(shù)據(jù)(包HTFTP頭)被另外一個協(xié)議(在這里是UDP)封裝,然后下一個(IP),直重復下去,直到硬件(物理)層(這里是以人網(wǎng))。當另外一臺機器接收到包,哽件先剝?nèi)ヒ蕴W(wǎng)頭,內(nèi)核剝?nèi)P和UDP頭,TFTP程序再剝?nèi)FTP頭,最后得到數(shù)據(jù)?,F(xiàn)在我們終丁講到聲名狼藉的網(wǎng)絡(luò)分層模型(LayeredNetworkModel)o這種網(wǎng)絡(luò)模型在描述網(wǎng)絡(luò)系統(tǒng)上相對其它模型有很多優(yōu)點。例如,你可以寫一個套接字程序而不用關(guān)心數(shù)據(jù)的物理傳輸(串行II,以太網(wǎng),連接單元接II(AUI)還是其它介質(zhì)),因為底層的程序會為你處理它們。實際的網(wǎng)絡(luò)硬件和拓撲刈丁程序員來說是透明的。不說其它廢話了,我現(xiàn)在列出

19、整個層次模型。如果你要參加網(wǎng)絡(luò)考試,可一定要記住:應(yīng)用層(Application)表示層(Presentation)會話層(Session)傳輸層(Transport)網(wǎng)絡(luò)層(Network)數(shù)據(jù)鏈路層(DataLink)物理層(Physical)物理層是破件(串II,以太網(wǎng)等等)。應(yīng)用層是和破件層相隔最遠的-它是用戶和網(wǎng)絡(luò)交互的地方。這個模型如此通用,如果你想,你可以把它作為修車指南。把它對應(yīng)到Unix,結(jié)果是:應(yīng)用層(ApplicationLayer)(telnet,ftp,等等)傳輸層(Host-to-HostTransportLayer)(TCP.UDP)Internet層(Inter

20、netLayer)(IP和路由)網(wǎng)絡(luò)訪問層(NetworkAccessLayer)(網(wǎng)絡(luò)層,數(shù)據(jù)鏈路層和物理層)現(xiàn)在,你可能看到這些層次如何協(xié)調(diào)來封裝原始的數(shù)據(jù)了。看看建立一個簡單的數(shù)據(jù)包有多少工作?哎呀,你將不得不使用”cat”來建立數(shù)據(jù)包頭!這僅僅是個玩笑。對于流式套接字你要作的是send()發(fā)送數(shù)據(jù)。対數(shù)據(jù)報式套接字,你按照你選擇的方式封裝數(shù)據(jù)然后使用sendto()o內(nèi)核將為你建立傳輸層和Internet層,碩件完成網(wǎng)絡(luò)訪問層。這就是現(xiàn)代科技。現(xiàn)在結(jié)束我們的網(wǎng)絡(luò)理論速成班。哦,忘記告訴你關(guān)丁路由的事情了。但是我不準備談它,如果你真的關(guān)心,那么參考IPRFCo結(jié)構(gòu)體終丁談到編程了。在這章

21、,我將談到被套接字用到的各種數(shù)據(jù)類型。因為它們中的一些內(nèi)容很重要了。首先是簡單的一個:socket描述符。它是卞而的類型:int僅僅是一個常見的into從現(xiàn)在起,事情變得不可思議了,而你所需做的就是繼續(xù)看下去。注意這樣的事實:有兩種字節(jié)排列順序:重要的字節(jié)(有時叫”octet,即八位位組)在前面,或者不重要的字節(jié)在前而。前一種叫“網(wǎng)絡(luò)字節(jié)順序(NetworkByteOrder)”。有些機器在內(nèi)部是按照這個順序儲存數(shù)據(jù),而另外一些則不然。當我說某數(shù)據(jù)必須按照NBO順序,那么你要調(diào)用函數(shù)(例如htons()來將它從本機字節(jié)順序(HostByteOrder)轉(zhuǎn)換過來。如杲我沒有提到NBO,那么就讓它

22、保持本機字節(jié)順序。我的第一個結(jié)構(gòu)(在這個技術(shù)手冊TM中)-structsockaddr.o這個結(jié)構(gòu)為許多類型的套接字儲冇套接字地址信息:structsockaddrunsignedshortsa_family;/*地址家族.AF_xxx*/charsa_data14;/*14字節(jié)協(xié)議地址*/;sa_family能夠是各種各樣的類型,但是在這篇文章中都是”AF_INET”。sa_data包含套接字中的目標地址和端II信息。這好像有點不明智。為了處理structsockaddr,程序員創(chuàng)造了一個并列的結(jié)構(gòu):structsockaddr_in(in代表Interneto)structsockaddr

23、_inshortintsin_family;/*通信類型*/unsignedshortintsin_port;/*端丨I*/stmctin_addrsin_addr;/*Internet地址*/unsignedcharsin_zero8;/*與sockaddr結(jié)構(gòu)的長度相同*/;用這個數(shù)據(jù)結(jié)構(gòu)可以輕松處理套接字地址的基本元素。注意sin.zero(它被加入到這個結(jié)構(gòu),并且長度和stnictsockaddr一樣)應(yīng)該使用函數(shù)bzeroQ或memset()來全部置零。同時,這一重要的字節(jié),一個指向sockaddrjn結(jié)構(gòu)體的指針也可以被指向結(jié)構(gòu)體sockaddr并且代替它。這樣的話即使socket

24、()想要的是structsockaddr*,你仍然可以使用structsockaddrjn,并且在最后轉(zhuǎn)換。同時,注意sin_family和structsockaddr中的sa_family一致并能夠設(shè)置為AF_INET。最后,sin_port和sin_addr必須是網(wǎng)絡(luò)字節(jié)順序(NetworkByteOrder)!你也許會反對道:“但是,怎么讓整個數(shù)據(jù)結(jié)構(gòu)structin_addrsin_addr按照網(wǎng)絡(luò)字節(jié)順序呢?要知道這個問題的答案,我們就要仔細的看一看這個數(shù)據(jù)結(jié)構(gòu):structin_addr.有這樣一個聯(lián)合(unions):/*Internet地址(一個與歷史有關(guān)的結(jié)構(gòu))*/struc

25、tin_addrunsignedlongs_addr:;它曾經(jīng)是個最壞的聯(lián)合,但是現(xiàn)在那些口子過去了。如果你聲明“ina是數(shù)據(jù)結(jié)構(gòu)stnictsockaddrjn的實例,那么ina.sin_addr.s_addr就儲存4字節(jié)的IP地址(使用網(wǎng)絡(luò)字節(jié)順序)。如果你不幸的系統(tǒng)使用的還是恐怖的聯(lián)合structin_addr,你還是可以放心4字節(jié)的IP地址并且和上面我說的一樣(這是因為使用了“#deflne”。)本機轉(zhuǎn)換我們現(xiàn)在到了新的章節(jié)。我們曾經(jīng)講了很多網(wǎng)絡(luò)到本機字節(jié)順序的轉(zhuǎn)換,現(xiàn)在可以實踐了!你能夠轉(zhuǎn)換兩種類型:short倆個字節(jié))和long(四個字節(jié))。這個函數(shù)対于變最類型unsigned也適

26、用。假設(shè)你想將short從本機字節(jié)順序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序。用”h”表示”本機(host)”,接著是to”,然后用n表示網(wǎng)絡(luò)(network),最后用s表示short:h-to-n-s,或者htons()(HosttoNetworkShort)o太簡單了如果不是太傻的話,你一定想到了由”n”,“h,s“,和T形成的正確組合,例如這里肯定沒有stolh()(ShorttoLongHost)函數(shù),不僅在這里沒有,所苗場合都沒有。但是這里有:htons()HosttoNetworkShorthtonl()HosttoNetworkLongntohs()NetworktoHostShortntohl()

27、NetworktoHostLong現(xiàn)在,你可能想你己經(jīng)知道它們了。你也可能想:“如果我想改變char的順序要怎么辦呢?”但是你也許馬上就想到,“用不著考慮的”。你也許會想到:我的68000機器己經(jīng)使用了網(wǎng)絡(luò)字節(jié)順序,我沒有必要去調(diào)用htonl()轉(zhuǎn)換IP地址。你可能是対的,但是當你移植你的程序到別的機器上的時候,你的程序?qū)⑹???梢浦残?!這里是Unix世界!記?。涸谀銓?shù)據(jù)放到網(wǎng)絡(luò)上的時候,確信它們是網(wǎng)絡(luò)字節(jié)順序的。最后一點:為什么在數(shù)據(jù)結(jié)構(gòu)structsockaddr_in中,sin_addr和sin_port需要轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序,而sin_family需不需要呢?答案是:sin_addr

28、和sin_port分別封裝在包的IP和UDP層。因此,它們必須要是網(wǎng)絡(luò)字節(jié)順序。但是sin_family域只是被內(nèi)核(kernel)使用來決定在數(shù)據(jù)結(jié)構(gòu)中包含什么類型的地址,所以它必須是本機字節(jié)順序。同時,sin_faniily沒有發(fā)送到網(wǎng)絡(luò)上,它們可以是本機字節(jié)順序。IP地址和如何處理它們現(xiàn)在我們很幸運,因為我們有很多的函數(shù)來方便地操作IP地址。沒有必要用手工計算它們,也沒有必要用”vv“操作來儲存成長整字型。首先,假設(shè)你已經(jīng)有了一個sockaddrjn結(jié)構(gòu)體ina,你有一個IP地址132.241.50”要儲存在其中,你就要用到函數(shù)inet_addr(),將IP地址從點數(shù)格式轉(zhuǎn)換成無符號長整

29、型。使用方法如卜:ina.sin_addr.s_addr=inet_addr(0);注意,inet_addr()返回的地址已經(jīng)是網(wǎng)絡(luò)字節(jié)格式,所以你無需再調(diào)用函數(shù)htonl()。我們現(xiàn)在發(fā)現(xiàn)上面的代碼片斷不是十分完整的,因為它沒有錯誤檢查。顯而易見,當inet_addr()發(fā)生錯誤時返回-io記住這些二進制數(shù)字?(無符號數(shù))僅僅和IP地址55相符合!這可是廣播地址!人錯特錯!記住要先進行錯誤檢查。好了,現(xiàn)在你可以將IP地址轉(zhuǎn)換成長整型了。有沒有其相反的方法呢?它可以將一個in.addr結(jié)構(gòu)體輸出成點數(shù)格式?這樣的話,你就要用到函數(shù)inet_ntoa()(ntoa的含義是”networktoas

30、cii),就像這樣:printf(,%s,inet_ntoa(ina.sin_addr);它將輸岀IP地址。需要注意的是inet_ntoa()將結(jié)構(gòu)體in-addr作為一個參數(shù),不是長整形。同樣需要注意的是它返回的是一個指向一個字符的指針。它是一個由inet_ntoa()控制的靜態(tài)的固定的指針,所以每次調(diào)用inet_ntoa(),它就將覆蓋上次調(diào)用時所得的IP地址。例如:char*al,*a2;al=inet_ntoa(inal.sin_addr);/*這是*/a2=inet_ntoa(ina2.sin_addr);/*這是0*/print”address1:%snal);printf(Had

31、dress2:%sn*a2);輸出如F:address1:0address2:0假如你需要保存這個IP地址,使用strcopyO函數(shù)來指向你自己的字符指針。上面就是關(guān)丁這個主題的介紹。稍后,你將學習將一個類似的字符串轉(zhuǎn)換成它所對應(yīng)的IP地址(查閱域名服務(wù),稍后)。socket()函數(shù)我想我不能再不提這個了一下面我將討論一下socket()系統(tǒng)調(diào)用。下面是詳細介紹:#include;#include:intsocket(intdomain,inttype,intprotocol);但是它們的參數(shù)是什么?首先,domain應(yīng)該設(shè)置成“AF_INET“,就象上而的數(shù)據(jù)結(jié)構(gòu)structsockaddr

32、_in中一樣。然后,參數(shù)type告訴內(nèi)核是SOCK_STREAM類型還是SOCK_DGRAM類型。最后,把protocol設(shè)置為“0。(注意:有很多種domain、type,我不可能列出了,請看socket()的man幫助。當然,還有一個更好的方式去得到protocoL同時請查閱getprotobyname()的man幫助。)socketO只是返回你以后在系統(tǒng)調(diào)用種可能用到的socket描述符,或者在錯誤的時候返回-1。全局變屋errno中將儲存返回的錯誤值。(請參考peiror()的man幫助。)bind()函數(shù)一旦你有一個套接字,你可能要將套接字和機器上的一定的端II關(guān)聯(lián)起來。(如果你想用

33、listen。來偵聽一定端II的數(shù)據(jù),這是必要一步-MUD告訴你說用命令lnetx.y.z6969“。)如杲你只想用connect。,那么這個步驟沒有必要。但是無論如何,請繼續(xù)讀下去。這里是系統(tǒng)調(diào)用bind()的人概:#include;#include:intbind(intsockfd,structsockaddr*my_addr,intaddrlen);sockfd是調(diào)用socket返回的文件描述符。my_addr是指向數(shù)據(jù)結(jié)構(gòu)structsockaddr的指針,它保存你的地址(即端II和IP地址)信息。addrlen設(shè)置為sizeof(structsockaddr)簡單得很不是嗎?再看看

34、例子:#include;#include;#include;#defineMYPORT3490main()intsockfd:stmctsockaddr_inmy_addr;sockfd=socket(AF_INET.SOCK.STREAM,0):/*需耍錯誤檢查*/my_addr.sin_family=AF_INET:/*hostbyteorder*/my_addr.sin_port=htons(MYPORT);/*short,networkbyteorder*/my_addr.sin_addr.s_addr=inet_addr(0);bzero(&(my_addr.sin_zero),;/

35、*zerotherestofthestruct*/*dontforgetyourerrorcheckingforbind():*/bind(sockfd.(structsockaddr*)&:my_addr.sizeof(structsockaddr);這里也有要注意的幾件事情。my_addr.sin_port是網(wǎng)絡(luò)字節(jié)順序,my_addr.sin_addr.s_addr也是的。另外要注意到的事情是因系統(tǒng)的不同,包含的頭文件也不盡相同,請查閱本地的man幫助文件。在bind()主題中最后要說的話是,在處理自己的IP地址和/或端II的時候,何些工作是可以自動處理的。my_addr.sin_

36、port=0:/*隨機選擇一個沒有使用的端1-1*/my_addr.sin_addr.s_addr=INADDR.ANY:/*使用自己的IP地址*/通過將0賦給my_addr.sin_port,你告訴bind()自己選擇合適的端I。同樣,將my_addr.sin_addr.s_addr設(shè)置為INADDR_ANY,你告訴它自動填上它所運行的機器的IP地址。如果你一向小心謹慎,那么你可能注意到我沒有將INADDR_ANY轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序!這是因為我知道內(nèi)部的東西:INADDR_ANY實際上就是0!即使你改變字節(jié)的順序,0依然是0。但是完美主義者說應(yīng)該處處一致,INADDR_ANY或許是12呢?你

37、的代碼就不能工作了,那么就看下面的代碼:my_addr.sin_port=htons(0);/*隨機選擇一個沒有使用的端II*/my_addr.sin_addr.s_addr=htonl(INADDR_ANY);/*使用自己的IP地址*/你或許不相信,上而的代碼將可以隨便移植。我只是想指出,既然你所遇到的程序不會都運行使用htonl的INADDR_ANY。bind()在錯誤的時候依然是返回-1,并且設(shè)置全局錯誤變量errnoo在你調(diào)用bind()的時候,你耍小心的另一件事情是:不要采用小于1024的端II號。所有小于1024的端II號都被系統(tǒng)保留!你可以選擇從1024到65535的端I(如杲它

38、們沒有被別的程序使用的話)。你要注意的另外一件小事是:有時候你根本不需要調(diào)用它。如杲你使用connectO來和遠程機器進行通訊,你不需要關(guān)心你的本地端II號(就彖你在使用telnet的時候),你只要簡單的調(diào)用connect()就可以了,它會檢查套接字是否綁定端門,如果沒有,它會自己綁定一個沒有使用的本地端門。connect()程序現(xiàn)在我們假設(shè)你是個telnet程序。你的用戶命令你得到套接字的文件描述符。你聽從命令調(diào)用了socket。F步,你的用戶告訴你通過端I23(標準telnet端口)連接到”0。你該怎么做呢?幸運的是,你正在閱讀connectO-如何連接到遠程主機這一章。你可不想讓你的用戶

39、失望。connect)系統(tǒng)調(diào)用是這樣的:#include;#include;intconnect(intsockfd.structsockaddr水serv_addr、intaddrlen);sockfd是系統(tǒng)調(diào)用socketO返回的套接字文件描述符。serv_addr是保存著目的地端II和IP地址的數(shù)據(jù)結(jié)構(gòu)stmctsockaddroaddrlen設(shè)置為sizeof(structsockaddr)想知道得更多嗎?讓我們來看個例子:#includestring#include;#include;#defineDESTJP0#defineDEST.PORT23main()intsockfd;st

40、ructsockaddr_indest_addr;/*li的地址*/sockfd=socket(AF_INET.SOCK.STREAM,0):/*錯誤檢查*/dest_addr.sin_family=AF_INET:/*hostbyteorder*/dest_addr.sin_port=htons(DEST_PORT);/*short,networkbyteorder*/dest_addr.sin_addr.s_addr=inet_addr(DEST_IP);bzero(&nmp;(dest_add匚sin_zero),;/*zerotherestofthestruct*/*dontforge

41、ttoerrorchecktheconnect()!*/connect(sockfd,(structsockaddr*)&dest_addr,sizeof(stmctsockaddr);再一次,你應(yīng)該檢查connect()的返回值-它在錯誤的時候返回亠并設(shè)置全局錯誤變量errnoo同時,你可能看到,我沒有調(diào)用bind()o因為我不在乎本地的端II號。我只關(guān)心我要去那。內(nèi)核將為我選擇一個合適的端II號,而我們所連接的地方也自動地獲得這些信息。一切都不用擔心。listen。函數(shù)是換換內(nèi)容得時候了。假如你不希塑與遠程的一個地址相連,或者說,僅僅是將它踢開,那你就需要等待接入請求并且用各種方法處理它們

42、。處理過程分兩步:首先,你聽-listen(),然后,你接受-acceptO(請看下面的內(nèi)容)。除了要一點解釋外,系統(tǒng)調(diào)用listen也相當簡單。intIisten(intsockfd、intbacklog);sockfd是調(diào)用socketO返回的套接字文件描述符。backlog是在進入隊列中允許的連接數(shù)目。什么意思呢?進入的連接是在隊列中一直等待直到你接受(acceptO請看卜面的文章)連接。它們的數(shù)目限制丁隊列的允許。人多數(shù)系統(tǒng)的允許數(shù)目是20,你也可以設(shè)置為5到10。和別的函數(shù)一樣,在發(fā)生錯謀的時候返回1,并設(shè)置全局錯誤變量errno。你可能想彖到了,在你調(diào)用listen()前你或者要調(diào)

43、用bind()或者讓內(nèi)核隨便選擇一個端II。如果你想偵聽進入的連接,那么系統(tǒng)調(diào)用的順序可能是這樣的:socket();bind();listen();/*accept()應(yīng)該在這*/因為它相當?shù)拿髁?,我將在這里不給出例子了。(在accept。那一章的代碼將更加完全。)真正麻煩的部分在accept()oaccept()函數(shù)準備好了,系統(tǒng)調(diào)用accept()會有點古怪的地方的!你可以想彖發(fā)生這樣的事情:有人從很遠的地方通過一個你在偵聽(listen()的端II連接(connect()到你的機器。它的連接將加入到等待接受(accept()的隊列中。你調(diào)用accept()告訴它你何空閑的連接。它將返回

44、一個新的套接字文件描述符!這樣你就有兩個套接字了,原來的一個還在偵聽你的那個端II,新的在準備發(fā)送(send()和接收(recv()數(shù)據(jù)。這就是這個過程!函數(shù)是這樣定義的:#include;intaccept(intsockfd,void*addnint*addrlen);sockfd相當簡單,是和listen()中一樣的套接字描述符。addr是個指向局部的數(shù)據(jù)結(jié)構(gòu)sockaddr_in的指針。這是要求接入的信息所要去的地方(你可以測定那個地址在那個端II呼叫你)。在它的地址傳遞給acceptZ前,addrlen是個局部的整形變吊,設(shè)置為sizeof(structsockaddr_in)oac

45、cept將不會將多余的字節(jié)給addr。如杲你放入的少些,那么它會通過改變addrlen的值反映出來。同樣,在錯誤時返回1,并設(shè)置全局錯誤變i:errnoo現(xiàn)在是你應(yīng)該熟悉的代碼片段。#includestring#include;#include;#defineMYPORT3490/*用戶接入端II*/#defineBACKLOG10/*多少等待連接控制*/main()intsockfd,new_fd;/*listenonsock_fd,newconnectiononnev_fd*/structsockaddr_inmy_addr:/*地址信息*/structsockaddr_intheir_a

46、ddr;/*connectorsaddressinformation*/intsin.size;sockfd=socket(AF_INET.SOCK.STREAM,0):/*錯誤檢查釘my_addr.sin_faniily=AF_INET:/*hostbyteorder*/my_add匚sin_port=htons(MYPORT);/*short,networkbyteorder*/my_add匚sin_addr.s_addr=INADDR_ANY:/*auto-fillwithmyIP*/bzero(&:(my_addr.sin_zcro),;/*zerotherestofthestr

47、uct*/*dontforgetyourerrorcheckingforthesecalls:*/bind(sockfd,(structsockaddr*)&my_addr,sizeof(structsockaddr);listen(sockfd,BACKLOG);sin.size=sizeof(structsockaddrjn);new_fd=accept(sockfd&:their_addn&sin_size);注意,在系統(tǒng)調(diào)用send()和recv()中你應(yīng)該使用新的套接字描述符new_fd如果你只想讓一個連接進來,那么你可以使用close()去關(guān)閉原來的文件描述符sockfd來避

48、免同一個端II更多的連接。send()andrccv()函數(shù)這兩個函數(shù)用丁流式套接字或者數(shù)據(jù)報套接字的通訊。如果你喜歡使用無連接的數(shù)據(jù)報套接字,你應(yīng)該看一看卜面關(guān)于sendto()和recvfrom()的章節(jié)。send()是這樣的:intsend(intsockfd,constvoid*msg,flags);sockfd是你想發(fā)送數(shù)據(jù)的套接字描述符(或者是調(diào)用socket()或者是accept()返回的。)msg是指向你想發(fā)送的數(shù)據(jù)的指針。len是數(shù)據(jù)的長度。扌巴flags設(shè)置為0就可以了。(詳細的資料請看send()的manpage)。這里是一些可能的例子:char*msg=Beejwash

49、ere!;intlen.bytes_sent;len=strlen(msg);bytes_sent=send(sockfd.msg,len,0);send()返回實際發(fā)送的數(shù)據(jù)的字節(jié)數(shù)-它可能小丁你要求發(fā)送的數(shù)目!注意,有時候你告訴它要發(fā)送一堆數(shù)據(jù)可是它不能處理成功。它只是發(fā)送它可能發(fā)送的數(shù)據(jù),然后希望你能夠發(fā)送其它的數(shù)據(jù)。記住,如果send()返回的數(shù)據(jù)和len不匹配,你就應(yīng)該發(fā)送其它的數(shù)據(jù)。但是這里也有個好消息:如果你要發(fā)送的包很小(小于人約1K),它可能處理讓數(shù)據(jù)一次發(fā)送完。最后要說得就是,它在錯誤的時候返回-1,并設(shè)置errnoorecv()函數(shù)很相似:intrecv(intsockf

50、d.void*buf,intlen,unsignedintflags);sockfd是要讀的套接字描述符。buf是要讀的信息的緩沖。len是緩沖的最人長度。flags可以設(shè)置為0。(請參考recv()的manpage。)recv()返回實際讀入緩沖的數(shù)據(jù)的字節(jié)數(shù)?;蛘咴阱e誤的時候返回-1,同時設(shè)置errnoo很簡單,不是嗎?你現(xiàn)在可以在流式套接字上發(fā)送數(shù)據(jù)和接收數(shù)據(jù)了。你現(xiàn)在是Unix網(wǎng)絡(luò)程序員了!sendto()和recvfrom()函數(shù)“這很不錯啊”,你說,“但是你還沒有講無連接數(shù)據(jù)報套接字呢?”沒問題,現(xiàn)在我們開始這個內(nèi)容。既然數(shù)據(jù)報套接字不是連接到遠程主機的,那么在我們發(fā)送一個包Z前需

51、要什么信息呢?不錯,是目標地址!看看下面的:intsendto(intsockfd.constvoid*msg,intlen.unsignedintflags,conststructsockaddr*to,inttolen);你已經(jīng)看到了,除了另外的兩個信息外,其余的和函數(shù)send()是一樣的。to是個指向數(shù)據(jù)結(jié)構(gòu)structsockaddr的指針,它包含了目的地的IP地址和端II信息。tolen叫.以簡單地設(shè)置為sizeof(structsockaddr)。和函數(shù)send()類似,sendto()返回實際發(fā)送的字節(jié)數(shù)(它也可能小于你想要發(fā)送的字節(jié)數(shù)!),或者在錯誤的時候返回-1相似的還有函數(shù)

52、recv()和recvfrom()orecvfrom()的定義是這樣的:intrecvfrom(intsockfd,void*len.unsignedintflags,structsockaddr*fromlen):又一次,除了兩個增加的參數(shù)外,這個函數(shù)和recv()也是一樣的。from是一個指向局部數(shù)據(jù)結(jié)構(gòu)structsockaddr的指針,它的內(nèi)容是源機器的IP地址和端丨I信息。fromlen是個int型的局部指針,它的初始值為sizeof(structsockaddr)o函數(shù)調(diào)用返回后,fromlen保存著實際儲存在from中的地址的長度。recvfromO返回收到的字節(jié)長度,或者在發(fā)生

53、錯誤后返回-1。記住,如果你用connectO連接一個數(shù)據(jù)報套接字,你可以簡單的調(diào)用send()和recv()來滿足你的要求。這個時候依然是數(shù)據(jù)報套接字,依然使用UDP,系統(tǒng)套接字接II會為你自動加上了目標和源的信息。close()和shutdown()函數(shù)你已經(jīng)整天都在發(fā)送(send()和接收(recv()數(shù)據(jù)了,現(xiàn)在你準備關(guān)閉你的套接字描述符了。這很簡單,你可以使用一般的Unix文件描述符的close()函數(shù):close(sockfd);它將防止套接字上更多的數(shù)據(jù)的讀寫。任何在另一端讀寫套接字的企圖都將返回錯誤信息。如果你想在如何關(guān)閉套接字上有多一點的控制,你可以使用函數(shù)shutdown(

54、)o它允許你將一定方向上的通訊或者雙向的通訊(就彖closeO-樣)關(guān)閉,你可以使用:intshutdown(how);sockfd是你想要關(guān)閉的套接字文件描述復。how的值是卜而的其中Z:0-不允許接受1-不允許發(fā)送2-不允許發(fā)送和接受(和close()一樣)shutdown。成功時返回0,失敗時返回-1(同時設(shè)置erniOo)如果在無連接的數(shù)據(jù)報套接字中使用shutdown。,那么只不過是讓send()和recv()不能使用(記住你在數(shù)據(jù)報套接字中使用了connect后是可以使用它們的)。getpeernanie()函數(shù)這個函數(shù)太簡單了。它A簡單了,以至我都不想單列一章。但是我還是這樣做了

55、。函數(shù)getpeernameO告訴你在連接的流式套接字上誰在另外一邊。函數(shù)是這樣的:#include;intgetpeername(intsockfd,structsockaddr*addrlen);sockfd是連接的流式套接字的描述符。addr是一個指向結(jié)構(gòu)structsockaddr(或者是structsockaddr_in)的指針,它保存著連接的另一邊的信息。addrlen是一個int型的指針,它初始化為sizeof(stmctsockaddr)。函數(shù)在錯誤的時候返回-1,設(shè)置相應(yīng)的ermoo一旦你獲得它們的地址,你可以使用inet_ntoa()或者gethostbyaddr()打印或

56、者獲得更多的信息。但是你不能得到它的帳號。(如果它運行著愚奩的守護進程,這是可能的,但是它的討論已經(jīng)超岀了本文的范圍,請參考RFC-1413以獲得更多的信息。)gethostname()函數(shù)甚至比getpeernameO還簡單的函數(shù)是gethostname()o它返回你程序所運行的機器的主機名字。然后你可以使用gethostbyname()以獲得你的機器的IP地址。下面是定義:#include;intgethostname(char*hostname,size_tsize);參數(shù)很簡單:hostname是一個字符數(shù)組指針,它將在函數(shù)返回時保存主機名。size是hostname數(shù)組的字節(jié)長度。函

57、數(shù)調(diào)用成功時返回0,失敗時返回-1,并設(shè)置errnoo域名服務(wù)(DNS)如果你不知道DNS的意思,那么我告訴你,它代表域名服務(wù)(DomainNameService)它主要的功能是:你給它一個容易記憶的某站點的地址,它給你IP地址(然后你就可以使用bind().connect(),sendto()或者其它函數(shù))。當一個人輸入:$telnet能知道它將連接(connect。)到00%但是這是如何工作的呢?你可以調(diào)用函數(shù)gethostbyname():#include:structhostent*gethostbyname(constchar*name);很明白的是,它返回一個指向stmcthost

58、ent的指針。這個數(shù)據(jù)結(jié)構(gòu)是這樣的:stmcthostentchar*h_name;char*h_aliases;Sck0072010-1-13第 頁共26頁SckOO72010-1-13客戶服務(wù)器背景知識第14頁共26頁inth_addrtype:inth_length;char*h_addr_list;#defineh_addrh_addr_listO這里是這個數(shù)據(jù)結(jié)構(gòu)的詳細資料:structhostent:h_name-地址的正式名稱。h.aliases-空字節(jié)地址的預(yù)備名稱的指針。h_addrtype-J也址類型;通常是AFJNETohjength-地址的比特長度。h_addr_lis

59、t-零字節(jié)主機網(wǎng)絡(luò)地址指針。網(wǎng)絡(luò)字節(jié)順序。h_addr-h_addr_list中的第一地址。gethostbyname()成功時返回一個指向結(jié)構(gòu)體hostent的指針,或者是個空(NULL)指針。(但是和以前不同,不設(shè)置errno,h_errno設(shè)置錯誤信息。請看下而的herror()o)但是如何使用呢?有時候(我們可以從電腦手冊中發(fā)現(xiàn)),向讀者灌輸信息是不夠的。這個函數(shù)可不彖它看上去那么難用。這里是個例子:#include;#include;#include:#include;#include;#include:intmain(intargc,char*argv)stmcthostent*h

60、;if(argc!=2)/*檢查命令行*/fprintf(stderr,Husage:getipaddressnH);exit(l);if(h=gethostbyname(argv1)=NULL)/*取得地址信息*/herror(HgethostbynameH);exit(l);printf(HHostname:%snn,h-;h_name);printf(HIPAddress:%sn,inet_ntoa(*(structin_addr*)h-;h_addr);return0;在使用gethostbyname()的時候,你不能用perror()打印錯誤信息(因為errno沒有使用),你應(yīng)該調(diào)用

溫馨提示

  • 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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論