計(jì)算機(jī)網(wǎng)絡(luò)課件第3章 WinSock編程初步_第1頁
計(jì)算機(jī)網(wǎng)絡(luò)課件第3章 WinSock編程初步_第2頁
計(jì)算機(jī)網(wǎng)絡(luò)課件第3章 WinSock編程初步_第3頁
計(jì)算機(jī)網(wǎng)絡(luò)課件第3章 WinSock編程初步_第4頁
計(jì)算機(jī)網(wǎng)絡(luò)課件第3章 WinSock編程初步_第5頁
已閱讀5頁,還剩85頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

第3章WinSock編程初步WinSock是Windows操作系統(tǒng)的網(wǎng)絡(luò)編程接口規(guī)范,其全稱為WindowsSockets。WinSock規(guī)范是微軟以BSDUNIX的BerkeleySockets規(guī)范為基礎(chǔ)開發(fā)定義的,從1991年問世到現(xiàn)在出現(xiàn)過兩個(gè)主版本——WinSock1和WinSock2。WinSock1只支持TCP/IP協(xié)議棧,最流行的版本是Winsock1.1;WinSock2完全兼容WinSock1,但支持多種協(xié)議(例如NETBIOS、IPX等),最流行的版本是Winsock2.2。3.1WinsockAPI函數(shù)WindowsSockets規(guī)范給出了一套庫函數(shù)的函數(shù)原型和函數(shù)功能說明,這些函數(shù)由底層網(wǎng)絡(luò)軟件供應(yīng)商實(shí)現(xiàn),供高層網(wǎng)絡(luò)應(yīng)用程序開發(fā)者使用;不僅包括了BerkeleySockets風(fēng)格的庫函數(shù),同時(shí)也提供了一套Windows所特有的擴(kuò)展庫函數(shù),使程序員能夠使用Windows系統(tǒng)的消息驅(qū)動(dòng)機(jī)制。應(yīng)用程序調(diào)用WinSock的API函數(shù)實(shí)現(xiàn)相互之間的通信,而WinSockAPI函數(shù)又利用下層Windows操作系統(tǒng)中的網(wǎng)絡(luò)通信協(xié)議和相關(guān)的系統(tǒng)調(diào)用實(shí)現(xiàn)其通信功能。物理線路應(yīng)用層協(xié)議操作系統(tǒng)網(wǎng)絡(luò)應(yīng)用程序計(jì)算機(jī)硬件與物理網(wǎng)絡(luò)設(shè)備網(wǎng)絡(luò)編程接口WinSock網(wǎng)絡(luò)通信協(xié)議TCP/IP網(wǎng)絡(luò)設(shè)備接口規(guī)范NDIS計(jì)算機(jī)A操作系統(tǒng)網(wǎng)絡(luò)應(yīng)用程序計(jì)算機(jī)硬件與物理網(wǎng)絡(luò)設(shè)備網(wǎng)絡(luò)編程接口WinSock網(wǎng)絡(luò)通信協(xié)議TCP/IP網(wǎng)絡(luò)設(shè)備接口規(guī)范NDIS計(jì)算機(jī)BWinsockAPI函數(shù)可分為兩大類與BSDSocket相兼容的基本函數(shù),例如用于創(chuàng)建套接字的socket()函數(shù),用于發(fā)送數(shù)據(jù)的send()函數(shù)等,它們不僅提供了與BSDSocket完全相同的功能,而且函數(shù)格式兼容,因此使用這類函數(shù)編寫的網(wǎng)絡(luò)應(yīng)用程序能很容易移植到LINUX、UNIX等采用BSDSocket的系統(tǒng)中;另一類是Windows擴(kuò)展函數(shù),這類函數(shù)都以WSA為前綴,主要是為便于程序員能充分利用Windows的消息驅(qū)動(dòng)機(jī)制編程而提供的。注意:熟練掌握常用WinSockAPI函數(shù)的功能和使用方法是利用WindowsSocket進(jìn)行網(wǎng)絡(luò)編程的基礎(chǔ)。要掌握WinSockAPI函數(shù),首先要記住該函數(shù)的名稱與函數(shù)的主要功能,其次要清楚各參數(shù)的類型及作用,以及函數(shù)返回值的類型及意義,最后還要掌握函數(shù)成功執(zhí)行的必要條件,了解造成函數(shù)不能成功執(zhí)行的常見原因。3.2Winsock開發(fā)組件和運(yùn)行組件Winsock有開發(fā)組件和運(yùn)行組件兩大部分開發(fā)組件是提供給程序員開發(fā)應(yīng)用程序使用的,包括常量定義、宏定義、有關(guān)數(shù)據(jù)結(jié)構(gòu)、函數(shù)調(diào)用接口原型,它是以C/C++頭文件的方式提供;運(yùn)行組件是指包含有WinsockAPI函數(shù)的動(dòng)態(tài)鏈接庫,應(yīng)用程序運(yùn)行時(shí)必須裝入該動(dòng)態(tài)鏈接庫才能調(diào)用相應(yīng)的函數(shù)實(shí)現(xiàn)網(wǎng)絡(luò)通信功能,例如支持Winsock2.2的動(dòng)態(tài)鏈接庫文件是WSOCKE32.DLL。說明:動(dòng)態(tài)鏈接庫動(dòng)態(tài)鏈接庫(DynamicLinkLibrary,DLL)是一種包含有可供多個(gè)程序同時(shí)使用的函數(shù)和全局變量的庫,應(yīng)用程序在運(yùn)行過程中可動(dòng)態(tài)裝入和連接DLL并直接使用其中的函數(shù)和全局變量。多個(gè)應(yīng)用程序可同時(shí)使用內(nèi)存中的單個(gè)DLL拷貝,DLL是一種代碼共享的方式。DLL可以提高程序模塊的靈活性,它本身是可以單獨(dú)設(shè)計(jì)、編譯和調(diào)試的。DLL的編制與具體的編程語言及編譯器無關(guān),只要遵循約定的DLL接口規(guī)范和調(diào)用方式,不管是用何種語言編寫的DLL,各種語言的程序都可使用。WinSock開發(fā)組件是提供給程序員開發(fā)應(yīng)用程序使用的,最主要的部分是包含有程序開發(fā)所需的常量定義、宏定義、有關(guān)的數(shù)據(jù)結(jié)構(gòu)定義和函數(shù)調(diào)用接口的原型聲明等的C/C++頭文件,除此之外還包括WindowsSockets實(shí)現(xiàn)文檔以及WinSock動(dòng)態(tài)鏈接庫的引入庫。運(yùn)行組件則是指包含有WinSockAPI函數(shù)的WinSock動(dòng)態(tài)鏈接庫。不同版本的WinSock所對(duì)應(yīng)的開發(fā)組件的相關(guān)的文件名字是不同的。WinSock1對(duì)應(yīng)的頭文件是WinSock.h,導(dǎo)入庫文件是Wsock32.libWinSock2的頭文件是WinSock2.h,導(dǎo)入庫文件則是Ws2_32.lib。由于WinSock2完全兼容WinSock1,因此,當(dāng)系統(tǒng)中安裝的是WinSock2時(shí),程序中即可以使用WinSock1的頭文件和導(dǎo)入庫也可以使用WinSock2的頭文件和導(dǎo)入庫,但如果要使用只有WinSock2才有的功能,那就只能使用WinSock2的頭文件和導(dǎo)入庫。VisualC++使用WinSock的步驟1.包含WinSock頭文件:在程序文件首部使用編譯預(yù)處理命令“#include”,將WinSock頭文件包含進(jìn)來。例如:#include<WinSock2.h>2.鏈接WinSock導(dǎo)入庫,有兩種方式:通過在項(xiàng)目屬性頁中的“配置屬性\鏈接器\輸入”的“附加依賴項(xiàng)”中直接添加導(dǎo)入庫名字在程序中使用預(yù)處理命令“#pragmacomment”。例如,程序要使用WinSock2時(shí),可使用如下預(yù)處理命令:

#pragmacomment(lib,"Ws2_32.lib")加載WinSock動(dòng)態(tài)鏈接庫:WinSock庫函數(shù)是以動(dòng)態(tài)連接庫的形式實(shí)現(xiàn)的,因此,在程序中調(diào)用WinSock函數(shù)時(shí),必須已經(jīng)加載了WinSock動(dòng)態(tài)鏈接庫。加載WinSock動(dòng)態(tài)鏈接庫使用WSAStartup()函數(shù),函數(shù)原型如下:intWSAStartup(WORDwVersionRequested,//版本號(hào)LPWSADATAlpWSAData//);函數(shù)返回值是一個(gè)整數(shù),函數(shù)調(diào)用成功則返回0,如果不成功則返回則返回一個(gè)代表失敗原因的代碼。函數(shù)參數(shù):wVersionRequested:該參數(shù)是一個(gè)雙字節(jié)類型數(shù)值,用于指明程序中要使用的WinSock庫的版本號(hào),其中高位字節(jié)指定副版本、低位字節(jié)指定主版本。

指定該參數(shù)值時(shí)可使用16進(jìn)制方式給出,比如要加載WinSock2.2版,該值可設(shè)定為0x0202,但更常用的方法則是使用宏MAKEWORD(X,Y),參數(shù)X為副版本號(hào),Y為主版本號(hào)。lpWSAData:用于返回關(guān)于使用的WinSock版本的詳細(xì)信息,它是一個(gè)指向WSADATA結(jié)構(gòu)體變量的指針。WSADATA結(jié)構(gòu)體的定義如下:#defineWSADESCRIPTION_LEN256#defineWSASYSSTATUS_LEN128TypedefstructWSAData{WORD

wVersion;//期望程序使用的WinSock版本號(hào)WORD

wHighVersion;//加載的Winsock庫支持的最高版本號(hào)char

szDescription[WSADESCRIPTION_LEN+1];//Winsock庫說明char

szSystemStatus[WSASYSSTATUS_LEN+1];//狀態(tài)和配置信息//以下字段被Winsock2及其后版本忽略u(píng)nsignedshort

iMaxSockets;//能同時(shí)打開的socket的最大數(shù)unsignedshort

iMaxUdpDg;//可發(fā)的UDP送數(shù)據(jù)報(bào)的最大字節(jié)數(shù)

charFAR*

lpVendorInfo;//廠商指定信息}WSADATA;假如一個(gè)程序要使用2.2版本的Winsock,那么程序中可采用如下代碼加載Winsock動(dòng)態(tài)鏈接庫:WSADATDwsaData;interr=WSAStartup(MAKEWORD(2,2),&wsaData);if(err!=0){//Winsock初始化錯(cuò)誤處理代碼…}4.注銷WinSock動(dòng)態(tài)鏈接庫:通信工作完成后,程序關(guān)閉之前應(yīng)卸載WinSock動(dòng)態(tài)鏈接庫并釋放資源。注銷WinSock動(dòng)態(tài)鏈接庫使用WSACleanup:intWSACleanup(void);該函數(shù)無參數(shù),執(zhí)行后將返回一個(gè)整數(shù)值,如果操作成功返回0,否則返回SOCKET_ERROR。常量SOCKET_ERROR在Winsok2.h(或Winsok.h)中定義,其對(duì)應(yīng)值為-1。對(duì)應(yīng)于一個(gè)程序中的每一次WSAStartup()調(diào)用,都應(yīng)該有一個(gè)WSACleanup()調(diào)用。3.3網(wǎng)絡(luò)字節(jié)順序字節(jié)順序是指計(jì)算機(jī)在存儲(chǔ)由多個(gè)字節(jié)組成的數(shù)據(jù)(主要是整數(shù))時(shí)各字節(jié)在內(nèi)存中的存放順序。不同體系結(jié)構(gòu)的計(jì)算機(jī),其字節(jié)順序是不同的:有些計(jì)算機(jī)是高序字節(jié)存放在內(nèi)存低地址處,低序字節(jié)存放內(nèi)存高地址處,這被稱為大端字節(jié)順序(BigEndian);而另外一些計(jì)算機(jī)則相反,采用小端字節(jié)順序(LittleEndian

),將低字節(jié)數(shù)據(jù)存放在內(nèi)存低地址處,高字節(jié)數(shù)據(jù)存放在內(nèi)存高地址處。例如,我們常見得基于X86平臺(tái)的PC機(jī)是小端字節(jié)序的,而PowerPC系列的CPU大多情況下則是大端字節(jié)順序。例:在內(nèi)存中雙字(DWORD)0x01020304的存儲(chǔ)方式

:內(nèi)存地址4000400140024003LittleEndian04030201BigEndian01020304具體計(jì)算機(jī)中的多字節(jié)數(shù)據(jù)的存儲(chǔ)順序通常稱為主機(jī)字節(jié)順序。主機(jī)字節(jié)順序采用大端字節(jié)順序的計(jì)算機(jī)和主機(jī)字節(jié)順序采用小端字節(jié)順序的計(jì)算機(jī)在通過網(wǎng)絡(luò)交換數(shù)據(jù)時(shí),如果不作任何處理,將會(huì)出現(xiàn)嚴(yán)重問題。例如,一臺(tái)使用PowerPC系列的CPU、運(yùn)行UNIX的服務(wù)器發(fā)送一個(gè)16位數(shù)據(jù)0X1234到一臺(tái)采用INTEL酷睿i5系列CPU運(yùn)行Windows7的PC機(jī)時(shí),這個(gè)16位數(shù)據(jù)將被INTEL的CPU解釋為0X3412,也就是將整數(shù)4660當(dāng)成了13330。為了解決這一問題,在編寫網(wǎng)絡(luò)程序時(shí),規(guī)定發(fā)送端要發(fā)送的多字節(jié)數(shù)據(jù)必須先轉(zhuǎn)換成與具體CPU無關(guān)的網(wǎng)絡(luò)字節(jié)順序在發(fā)送,接收端接收到數(shù)據(jù)后再將數(shù)據(jù)轉(zhuǎn)換為主機(jī)字節(jié)數(shù)據(jù)。網(wǎng)絡(luò)字節(jié)順序采用的是大端存儲(chǔ)方式。在指定套接字的網(wǎng)絡(luò)地址以及端口號(hào)時(shí)必須使用網(wǎng)絡(luò)字節(jié)順序,而由套接字函數(shù)返回的網(wǎng)絡(luò)字節(jié)順序的IP地址和端口號(hào)在本機(jī)處理時(shí),則需要轉(zhuǎn)換為主機(jī)字節(jié)順序。u_shorthtons(u_shorthostshort

);將一個(gè)16位的無符號(hào)短整型數(shù)據(jù)由主機(jī)字節(jié)順序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序。參數(shù)hostshort:一個(gè)待轉(zhuǎn)換的主機(jī)字節(jié)順序的無符號(hào)短整型數(shù)據(jù)。函數(shù)調(diào)用成功返回一個(gè)網(wǎng)絡(luò)字節(jié)順序的無符號(hào)短整型數(shù)。如果函數(shù)調(diào)用失敗,則返回SOCKET_ERROR,進(jìn)一步的出錯(cuò)信息可調(diào)用WSAGetLastError()獲取。u_shortntohs(u_shortnetshort

);該函數(shù)將一個(gè)16位的無符號(hào)短整型數(shù)據(jù)由網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換為主機(jī)字節(jié)數(shù)順序返回。參數(shù)netshort:一個(gè)待轉(zhuǎn)換的網(wǎng)絡(luò)字節(jié)數(shù)順序的無符號(hào)短整型數(shù)據(jù)。函數(shù)調(diào)用成功返回一個(gè)主機(jī)字節(jié)順序的無符號(hào)短整型數(shù)。如果函數(shù)調(diào)用失敗,則返回SOCKET_ERROR,進(jìn)一步的出錯(cuò)信息可調(diào)用WSAGetLastError()獲取。u_longhtonl(u_longhostlong

);將一個(gè)32位的無符號(hào)長整型數(shù)據(jù)由主機(jī)字節(jié)順序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序返回。參數(shù)hostlong

:一個(gè)待轉(zhuǎn)換的主機(jī)字節(jié)順序的無符號(hào)長整型數(shù)據(jù)。函數(shù)調(diào)用成功返回一個(gè)網(wǎng)絡(luò)字節(jié)順序的無符號(hào)長整型數(shù)。如果函數(shù)調(diào)用失敗,則返回SOCKET_ERROR,進(jìn)一步的出錯(cuò)信息可調(diào)用WSAGetLastError()獲取。u_longntohl(u_longnetlong

);該函數(shù)將一個(gè)32位的無符號(hào)長整型數(shù)據(jù)由網(wǎng)絡(luò)字節(jié)順序轉(zhuǎn)換為主機(jī)字節(jié)數(shù)順序返回。參數(shù)netlong:一個(gè)待轉(zhuǎn)換的網(wǎng)絡(luò)字節(jié)數(shù)順序的無符號(hào)長整型數(shù)據(jù)。函數(shù)調(diào)用成功返回一個(gè)主機(jī)字節(jié)順序的無符號(hào)長整型數(shù)。如果函數(shù)調(diào)用失敗,則返回SOCKET_ERROR,進(jìn)一步的出錯(cuò)信息可調(diào)用WSAGetLastError()獲取。例題:#include“winsock2.h”//使用WinSock2進(jìn)行開發(fā)需要用到頭文件<winsock.h>#include"stdio.h"#pragmacomment(lib,"ws2_32.lib")//加載庫文件“"ws2_32.lib"intmain(intargc,char**argv){

//初始化WinsockDLL

WSADATAwsaData;

if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)return0;

u_shortx,y=0x1234;//定義無符號(hào)短整型變量x、y,將y初始化為十六進(jìn)制數(shù)0x1234

x=htons(y);//將y的值轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序并將轉(zhuǎn)換結(jié)果存入變量x

printf("主機(jī)字節(jié)順序:%X\n網(wǎng)絡(luò)字節(jié)順序:%X\n",y,x);

u_longa,b=0x1122ABCD;//定義無符號(hào)長整型變量a、b,并將b初始化

a=htonl(b);//將b的值轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)順序并將轉(zhuǎn)換結(jié)果存入變量a

printf("主機(jī)字節(jié)順序:%X\n網(wǎng)絡(luò)字節(jié)順序:%X\n",b,a);

//注銷WinsockDLL

WSACleanup();

return0;}3.4WinSock的網(wǎng)絡(luò)地址表示在IP網(wǎng)絡(luò)環(huán)境下,對(duì)于一個(gè)通信進(jìn)程而言,必須明確三方面信息:進(jìn)程所在的主機(jī)IP地址:用于區(qū)分網(wǎng)絡(luò)中的不同主機(jī)通信所采用的協(xié)議:指明通信所使用的傳輸層協(xié)議是TCP還是UDP協(xié)議端口號(hào):用于區(qū)分同一主機(jī)中運(yùn)行的采用同一傳輸層協(xié)議的不同進(jìn)程。通過這三方面的信息可以唯一確定在網(wǎng)絡(luò)中參與通信的一個(gè)進(jìn)程,因此進(jìn)程的網(wǎng)絡(luò)地址可以使用三元組(協(xié)議,IP地址,端口號(hào))來標(biāo)識(shí)。在編寫網(wǎng)絡(luò)程序時(shí),可以通過創(chuàng)建不同類型的套接字來指明通信所使用的協(xié)議:流式套接字采用TCP協(xié)議,數(shù)據(jù)報(bào)套接字采用UDP協(xié)議。對(duì)IP地址和端口號(hào),WinSock定義了三種專門的地址結(jié)構(gòu)來描述,這三種結(jié)構(gòu)均繼承于BSDSocket規(guī)范,分別適用于不同的應(yīng)用環(huán)境。3.4.1地址結(jié)構(gòu)in_addr結(jié)構(gòu)structin_addr{union

{

struct{u_chars_b1,s_b2,s_b3,s_b4}S_un_b;

struct{u_shorts_w1,s_w2}S_un_w;

U_longS_addr;

}S_un;}該結(jié)構(gòu)只有一個(gè)共用體(union)類型的字段S_un,專門用來存儲(chǔ)32位的IP地址。仔細(xì)觀察共用體的定義可以發(fā)現(xiàn),共用體的三個(gè)成員分別為:由4個(gè)單字節(jié)字段組成的結(jié)構(gòu)體變量S_un_b,使用該成員可以方便地分別處理IP地址的4個(gè)字節(jié);由2個(gè)雙字字段組成的結(jié)構(gòu)體變量S_un_w,使用該成員可將IP地址的高16位和低16位分別作為無符號(hào)短整數(shù)處理;無符號(hào)長整數(shù)變量S_add,使用該成員可將IP地址作為一個(gè)無符號(hào)長整數(shù)處理。sockaddr_in結(jié)構(gòu)structsockaddr_in{shortsin_family;

//地址族,IP協(xié)議地址對(duì)應(yīng)的值為AF_INETu_shortsin_port;

//16位端口號(hào),需使用網(wǎng)絡(luò)字節(jié)順序structin_addrsin_addr;//32位IP地址,需使用網(wǎng)絡(luò)字節(jié)順序charsin_zero[8];

//保留不用}sockaddr結(jié)構(gòu)structsockaddr{u_shortsa_family;

//協(xié)議地址族charsa_data[14];

//協(xié)議地址}該結(jié)構(gòu)為通用套接字地址結(jié)構(gòu)。不管是BSDSockets規(guī)范還是WinSock規(guī)范,并不僅僅針對(duì)TCP/IP協(xié)議族的,它們?cè)谠O(shè)計(jì)時(shí)就考慮了要兼容現(xiàn)存的多個(gè)網(wǎng)絡(luò)通信協(xié)議族,比如IPX、NETBIOS等,不同的協(xié)議族其地址格式是完全不同的。Sockaddr結(jié)構(gòu)的設(shè)計(jì)就是為了兼容多個(gè)不同協(xié)議族的地址。當(dāng)程序使用TCP/IP協(xié)議時(shí),sockaddr結(jié)構(gòu)的第一個(gè)域sa_family應(yīng)填寫AF_INET,表示是IP協(xié)議族地址;第二個(gè)域sa_data[14]的前2個(gè)字節(jié)是端口號(hào),隨后4個(gè)字節(jié)是IP地址,余下的8個(gè)字節(jié)不用。編程時(shí),直接填寫第二個(gè)字節(jié)比較復(fù)雜,需要一種簡單方法為該地址結(jié)構(gòu)類型的變量賦值;觀察一下sockaddr_in結(jié)構(gòu),可以發(fā)現(xiàn)這兩個(gè)結(jié)構(gòu)存儲(chǔ)的內(nèi)容完全一致,因此實(shí)際為sockaddr結(jié)構(gòu)的變量賦值時(shí)可利用C/C++語言的強(qiáng)制類型轉(zhuǎn)換進(jìn)行。先定義一個(gè)sockaddr_in結(jié)構(gòu)類型的指針變量p,通過強(qiáng)制類型轉(zhuǎn)換讓p指向sockaddr結(jié)構(gòu)類型的變量,然后通過指針p可按sockaddr_in類型的各字段完成賦值。代碼如下:structsockaddra; structsockaddr_in*p; p=(sockaddr_in*)&a; p->sin_family=AF_INET; p->sin_port=54321; p->sin_addr=inet_addr("");函數(shù)inet_addr()將參數(shù)指定的點(diǎn)分十進(jìn)制表示的IP地址轉(zhuǎn)換為無符號(hào)長整型(usignedlong)。3.4.2地址轉(zhuǎn)換函數(shù)inet_addr函數(shù):將點(diǎn)分十進(jìn)制字符串表示的IP地址轉(zhuǎn)換為32位的無符號(hào)長整型數(shù),它是以網(wǎng)絡(luò)字節(jié)順序表示的32位二進(jìn)制IP地址。unsignedlonginet_addr(constchar*cp);該函數(shù)只有一個(gè)參數(shù)cp,它指向存放有一個(gè)點(diǎn)分十進(jìn)制表示的IP地址的字符串。當(dāng)字符串的形式為“a.b.c.d”時(shí),a、b、c、d分別代表IP地址的4個(gè)字節(jié);當(dāng)字符串形式為“a.b.c”時(shí),a、b分別對(duì)應(yīng)IP地址的高位兩個(gè)字節(jié),c對(duì)應(yīng)于后兩個(gè)字節(jié);當(dāng)字符串形式為“a.b”時(shí),a被解釋稱IP地址的最高一個(gè)字節(jié),而b則解釋為后面的三個(gè)字節(jié)24位;當(dāng)形式為“a”時(shí),a將直接被作為網(wǎng)絡(luò)字節(jié)順序的二進(jìn)制表示的IP地址返回。如果傳入的字符串是一個(gè)非法的IP地址,函數(shù)返回值是常量INADDR_NONE。intinet_aton(constchar*cp,structin_addr*inp);將參數(shù)cp指向的點(diǎn)分十進(jìn)制字符串表示的IP地址轉(zhuǎn)換為32位的無符號(hào)長整型數(shù),并存放于參數(shù)inp指向的in_addr結(jié)構(gòu)變量中。cp:與函數(shù)inet_addr的參數(shù)cp同;inp:指向in_addr結(jié)構(gòu)變量的指針,該結(jié)構(gòu)變量用來保存轉(zhuǎn)換后的IP地址。如果這個(gè)函數(shù)成功,函數(shù)的返回值非零。如果輸入地址不正確則會(huì)返回零。char*inet_ntoa(structin_addrin);將一個(gè)包含在in_addr結(jié)構(gòu)變量in中的長整型IP地址轉(zhuǎn)換點(diǎn)分十進(jìn)制形式。參數(shù)in:是一個(gè)保存有32位二進(jìn)制IP地址的in_addr結(jié)構(gòu)變量。函數(shù)調(diào)用成功返回一個(gè)字符指針,該指針指向一個(gè)char型緩沖區(qū),該緩沖區(qū)保存有由參數(shù)in的值轉(zhuǎn)換而來的點(diǎn)分十進(jìn)制表示的IP地址字符串。如果函數(shù)調(diào)用失敗,則返回一個(gè)空指針NULL。在VC++2017中使用上面三個(gè)函數(shù)地址轉(zhuǎn)換函數(shù)時(shí),編譯器將發(fā)出錯(cuò)誤警告并停止編譯,如果要關(guān)閉錯(cuò)誤警告繼續(xù)編譯,需要在程序首部,預(yù)處理命令#include“WinSock2.h”前添加如下宏定義:#define_WINSOCK_DEPRECATED_NO_WARNINGS或者使用以下預(yù)處理命令:#pragmawarning(disable:4996)當(dāng)然也可以在項(xiàng)目屬性中,將“配置屬性—C/C++—常規(guī)”中的“SDL檢查”設(shè)置為“否(sdl-)“。如下圖所示。這三個(gè)函數(shù)來自于早期的BSD套接字規(guī)范,并不支持目前已逐漸普及的IPv6的地址轉(zhuǎn)換,為了兼容IPv6的地址轉(zhuǎn)換,新規(guī)范引入了inet_pton()與inet_ntop()兩個(gè)函數(shù)來取代上面這三個(gè)函數(shù)。需要說明的是,這三個(gè)函數(shù)目前仍被廣泛使用,因此讀者也應(yīng)掌握這三個(gè)函數(shù)的用法。下面介紹新的地址轉(zhuǎn)換函數(shù)inet_pton()和inet_ntop(),使用這兩個(gè)函數(shù)時(shí)需要包含頭文件WS2tcpip.h。inet_pton()函數(shù)intinet_pton(intaf,constchar*src,void*dst);函數(shù)功能:參數(shù)af取值A(chǔ)F_INET時(shí),該函數(shù)將參數(shù)src指向的點(diǎn)分十進(jìn)制表示的IPv4地址轉(zhuǎn)換為32位的網(wǎng)絡(luò)字節(jié)順序的無符號(hào)長整型數(shù),并存放于參數(shù)dst指向的in_addr結(jié)構(gòu)變量中;參數(shù)af取值A(chǔ)F_INET6時(shí),該函數(shù)將參數(shù)src指向的冒號(hào)十六進(jìn)制表示的IPv6地址轉(zhuǎn)換為二進(jìn)制IPv6地址,并存放于參數(shù)dst指向的in6_addr結(jié)構(gòu)變量中。由于篇幅所限,本書暫不考慮IPv6網(wǎng)絡(luò)編程。函數(shù)參數(shù)af:使用的地址族,目前有兩個(gè)取值:AF_INET表示IPv4,AF_INET6表示IPv6。src:指向要轉(zhuǎn)換的字符串形式表示的地址。dst:指向用于保存轉(zhuǎn)換結(jié)果的地址結(jié)構(gòu)變量,IPv4時(shí)該結(jié)構(gòu)變量的類型為in_addr,IPv6則是in6_addr。返回值如果函數(shù)執(zhí)行成功將返回1,出錯(cuò)將返回-1并將錯(cuò)誤碼設(shè)置為EAFNOSUPPORT,如果參數(shù)af指定的地址族或src格式不對(duì),函數(shù)將返回0。如果函數(shù)執(zhí)行成功將返回1,出錯(cuò)將返回-1并將錯(cuò)誤碼設(shè)置為EAFNOSUPPORT,如果參數(shù)af指定的地址族或src格式不對(duì),函數(shù)將返回0。例:將IP地址“”轉(zhuǎn)換為無符號(hào)長整型數(shù)存放在in_addr型變量a中,然后按16進(jìn)制輸出該地址。in_addra;if(inet_pton(AF_INET,"",&a)==1){cout<<hex<<ntohl(a.S_un.S_addr)<<endl;//a中的值是網(wǎng)絡(luò)字節(jié)數(shù)按序的}inet_ntop()函數(shù)constchar*inet_ntop(intaf,constchar*src,char*dst,socklen_tcnt);函數(shù)功能:當(dāng)參數(shù)af取值A(chǔ)F_INET時(shí),將參數(shù)src指向的32位的無符號(hào)長整型數(shù)表示的IPv4地址轉(zhuǎn)換為點(diǎn)分十進(jìn)制,并復(fù)制到參數(shù)dst指向的存儲(chǔ)區(qū)中;參數(shù)af取值A(chǔ)F_INET6時(shí),該函數(shù)將參數(shù)src指向的128位二進(jìn)制表示的IPv6地址轉(zhuǎn)換為冒號(hào)十六進(jìn)制表示的字符串形式的IPv6地址,并復(fù)制到參數(shù)dst指向的存儲(chǔ)區(qū)中。函數(shù)參數(shù)af:使用的地址族,目前有兩個(gè)取值:AF_INET表示IPv4,AF_INET6表示IPv6。src:指向要轉(zhuǎn)換的整形數(shù)表示的地址。dst:指向用于保存轉(zhuǎn)換結(jié)果的存儲(chǔ)區(qū)。cnt:dst所指向的緩存區(qū)的大小。返回值如果函數(shù)執(zhí)行成功將返回存儲(chǔ)轉(zhuǎn)換結(jié)果的緩沖區(qū)的首地址,否則返回NULL。例:in_addra;charip[20];a.S_un.S_addr=htonl(0xc0a80101);if(inet_ntop(AF_INET,&a,ip,sizeof(ip))!=NULL) cout<<ip<<endl;3.5WinSock的錯(cuò)誤處理WinSock函數(shù)在執(zhí)行結(jié)束時(shí)都會(huì)返回一個(gè)值,如果函數(shù)執(zhí)行成功,需要返回函數(shù)執(zhí)行結(jié)果的函數(shù)通常返回值就是執(zhí)行結(jié)果(該結(jié)果一般是用戶程序所需要的),而對(duì)無執(zhí)行結(jié)果的函數(shù),例如WSACleanup()函數(shù),則返回0;如果函數(shù)執(zhí)行不成功,則絕大多數(shù)函數(shù)都會(huì)返回SOCKET_ERROR,雖然通過該返回值可以知道函數(shù)調(diào)用不成功,但無法判斷函數(shù)不能成功執(zhí)行的原因,WinSock提供了兩個(gè)函數(shù)解決該問題。使用WSAGetLastError()函數(shù)可獲取上一次WinSock函數(shù)調(diào)用時(shí)的錯(cuò)誤代碼。該函數(shù)的函數(shù)原型如下:

intWSAGetLastError(void);函數(shù)的返回值是一個(gè)整數(shù),它是上一次調(diào)用WinSock函數(shù)出錯(cuò)時(shí)所出錯(cuò)誤對(duì)應(yīng)的錯(cuò)誤代碼。由于引起WinSock函數(shù)調(diào)用出錯(cuò)的原因很多,為了便于編寫出界面友好的應(yīng)用程序和方便編程者調(diào)試程序,WinSock規(guī)范列舉出了所有的出錯(cuò)原因并給每一種原因定義了一個(gè)整數(shù)類型(int)的編號(hào),該編號(hào)被稱作錯(cuò)誤碼。在頭文件Winsok2.h(Winsock1.1對(duì)應(yīng)的頭文件是winsock.h)中對(duì)每一個(gè)錯(cuò)誤碼都定義了一個(gè)對(duì)應(yīng)的符號(hào)常量。例如,當(dāng)客戶程序使用流式套接字試圖與遠(yuǎn)程服務(wù)器建立連接,而遠(yuǎn)程服務(wù)器并沒有響應(yīng)(可能是服務(wù)其軟件沒有啟動(dòng)),這時(shí)客戶程序中的connect()函數(shù)調(diào)用將會(huì)出錯(cuò),其錯(cuò)誤碼為10061,對(duì)應(yīng)的符號(hào)常量為WSAECONNREFUSED。3.6網(wǎng)絡(luò)配置信息查詢?cè)诰帉懢W(wǎng)絡(luò)程序時(shí),有時(shí)需要獲取計(jì)算機(jī)的名字、IP地址以及網(wǎng)絡(luò)服務(wù)或是網(wǎng)絡(luò)協(xié)議的相關(guān)信息等,為了實(shí)現(xiàn)這一目的,WinSock提供了一系列函數(shù)來幫助應(yīng)用程序完成相應(yīng)的信息查詢操作。3.6.1主機(jī)名字與IP地址查詢gethostname()該函數(shù)用來查詢本地計(jì)算機(jī)主機(jī)名字。函數(shù)原型intgethostname(char*name,intnamelen)函數(shù)參數(shù)Name:指向用于存放主機(jī)名字的緩沖區(qū),就是一字符數(shù)組。Namelen:是緩沖區(qū)的大小,也就是字符數(shù)組name的大小。返回值函數(shù)調(diào)用成功則返回0。函數(shù)調(diào)用失敗,則返回SOCKET_ERROR,進(jìn)一步的出錯(cuò)信息可調(diào)用WSAGetLastError()獲取。gethostbyname()根據(jù)主機(jī)名字查詢IP地址等信息。函數(shù)原型structhostent*gethostbyname(constchar*name);函數(shù)參數(shù)name:指向主機(jī)的名字的字符指針(字符串);返回值函數(shù)調(diào)用成功,函數(shù)將返回一個(gè)指向hostent結(jié)構(gòu)的指針,如果函數(shù)調(diào)用失敗,將返回一個(gè)空指針NULL,調(diào)用WSAGetLastError()可獲取引起函數(shù)失敗的錯(cuò)誤代碼。返回指針指向的hostent結(jié)構(gòu)由系統(tǒng)維護(hù),該結(jié)構(gòu)隨線程的終結(jié)而消亡。應(yīng)用程序不應(yīng)該試圖修改這個(gè)結(jié)構(gòu)或者釋放它的任何部分。由于gethostbyname()并不支持IPv6地址,因此新版規(guī)范中引入了該函數(shù)的替代版本getaddrinfo()。同inet_pton()與inet_ntop()一樣,在VC++2017中使用gethostbyname()也需要關(guān)閉錯(cuò)誤警告才能進(jìn)行編譯,關(guān)閉方法與使用inet_aton()函數(shù)時(shí)完全一樣。getaddrinfo()該函數(shù)用于從主機(jī)名字(或DNS域名)查詢主機(jī)的IP地址,也可以根據(jù)服務(wù)名查詢端口號(hào),它是協(xié)議無關(guān)的,既可以用于IPv4,也可用于IPv6。與inet_pton()和inet_ntop()函數(shù)一樣,程序中使用getaddrinfo()函數(shù)時(shí)也需要包含頭文件WS2tcpip.h。函數(shù)原型unsignedintgetaddrinfo(constchar*pNodeName,constchar*pServiceName,conststructaddrinfo*pHints,structaddrinfo**ppresult);函數(shù)參數(shù)pNodeName:指向主機(jī)的名字的字符指針(字符串),該名字除了可以是本機(jī)的名字外,還可以是網(wǎng)絡(luò)上任一臺(tái)主機(jī)的域名(DNS),也可以是IPv4的點(diǎn)分十進(jìn)制地址串或者IPv6的16進(jìn)制地址串。pServiceName:指定要查詢端口號(hào)對(duì)應(yīng)的服務(wù)名稱或字符串形式的端口號(hào),可以是ASCII形式的十進(jìn)制數(shù)符表示的端口號(hào),也可以是已定義的服務(wù)名稱,如ftp、http等。pHints:指向某個(gè)addrinfo結(jié)構(gòu)體的指針,該結(jié)構(gòu)用于填寫關(guān)于期望返回的信息類型的線索。例如,查詢服務(wù)的端口號(hào)時(shí),若指定的服務(wù)既使用TCP也使用UDP,把hints結(jié)構(gòu)中的ai_socktype成員設(shè)置成SOCK_DGRAM則函數(shù)將僅返回?cái)?shù)據(jù)報(bào)套接字的信息。若不需要提供任何線索時(shí),該參數(shù)可以為NULL。ppresult:該參數(shù)是一個(gè)指向addrinfo類型的指針變量的指針,它所指向的指針變量用于保存作為函數(shù)查詢結(jié)果的一個(gè)addrinfo結(jié)構(gòu)體鏈表的頭指針。返回值函數(shù)調(diào)用成功返回0,函數(shù)調(diào)用失敗將返回一個(gè)標(biāo)識(shí)失敗原因WinSock錯(cuò)誤代碼。函數(shù)的查詢結(jié)果保存在一個(gè)元素類型為structaddrinfo的鏈表中,該鏈表由系統(tǒng)維護(hù),函數(shù)只返回該鏈表的頭指針保存于參數(shù)ppresult所指向的指針變量中。structaddrinfo結(jié)構(gòu)體類型的定義如下:typedefstructaddrinfo{intai_flags;intai_family;intai_socktype;intai_protocol;size_tai_addrlen;char*ai_canonname;structsockaddr*ai_addr;structaddrinfo*ai_next;}ADDRINFOA,*PADDRINFOA;各成員的含義如下:ai_flags:標(biāo)志。較少使用,在此不作解釋,感興趣的讀者可自行查閱相關(guān)資料。ai_family:地址族,AF_INET表示IPv4,AF_INET6表示IPv6,AF_UNSPEC表示協(xié)議無關(guān)。ai_socktype:套接字類型,取值為SOCK_STREAM、SOCK_DGRAM等。ai_protocol:協(xié)議類型,取值為IPPROTO_IP,IPPROTO_IPV4,IPPROTO_UDP,IPPROTO_TCP等。ai_addrlen:成員ai_addr指向的地址緩沖區(qū)的長度。ai_canonname:主機(jī)的規(guī)范名。ai_addr:指向sockaddr結(jié)構(gòu)變量的指針。ai_next:addrinfo結(jié)構(gòu)類型的指針。在一個(gè)addrinfo結(jié)構(gòu)體變量構(gòu)成的鏈表中,ai_next指向鏈表中的下一個(gè)addrinfo結(jié)構(gòu),鏈表中最后一個(gè)結(jié)構(gòu)變量的ai_next應(yīng)設(shè)置為NULL。gethostbyaddr()根據(jù)傳入的IP地址查詢與該IP地址相對(duì)應(yīng)的信息。函數(shù)原型structhostent*gethostbyaddr(constchar*addr,intlen,inttype);函數(shù)參數(shù)addr:是指向網(wǎng)絡(luò)字節(jié)順序IP地址的指針。len:地址的長度,在AF_INET類型地址中為4。type:IPv4地址對(duì)應(yīng)的類型為AF_INET。返回值函數(shù)調(diào)用成功,返回一個(gè)指向hostent結(jié)構(gòu)的指針;調(diào)用失敗,將返回一個(gè)空指針NULL。例3.1使用gethostname()函數(shù)以及getaddrinfo()函數(shù)查詢本機(jī)的主機(jī)名稱及IPv4地址。3.6.2服務(wù)查詢服務(wù)器通常都能提供多種不同的服務(wù),比如Web服務(wù)、FTP文件服務(wù)、Telnet虛擬終端等,每種服務(wù)都要使用TCP或是UDP協(xié)議在一個(gè)知名端口號(hào)上偵聽客戶發(fā)來的服務(wù)請(qǐng)求。WinSock提供了一組函數(shù)可以查詢本機(jī)上所提供的服務(wù)、協(xié)議及端口號(hào),程序中可以使用這些函數(shù)來獲得服務(wù)對(duì)應(yīng)的端口,或者是獲得正在使用指定端口的服務(wù)。1)getservbyname()根據(jù)給定的服務(wù)名和所使用的協(xié)議名獲取服務(wù)的端口號(hào)等。structservent*getservbyname(constchar*name,constchar*proto);函數(shù)參數(shù)name:

一個(gè)指向服務(wù)名的指針。proto:指向協(xié)議名的指針(可選)。如果這個(gè)指針為空,函數(shù)返回第一個(gè)name與s_name或者某一個(gè)s_aliases匹配的服務(wù)條目。否則getservbyname()對(duì)name和proto都進(jìn)行匹配。返回值函數(shù)調(diào)用成功,函數(shù)將返回一個(gè)指向servent結(jié)構(gòu)的指針,該結(jié)構(gòu)由WinSock創(chuàng)建并管理,應(yīng)用程序不能修改或者釋放它的任何部分;函數(shù)調(diào)用不成功,將返回一個(gè)空指針NULLservent結(jié)構(gòu)的聲明如下:structservent{char*s_name;

//服務(wù)名。char**s_aliases;

//

一個(gè)以空指針結(jié)尾的服務(wù)別名隊(duì)列。Shorts_port;//連接該服務(wù)所需的端口號(hào),以網(wǎng)絡(luò)字節(jié)順序排列。char*s_proto;//連接該服務(wù)所用的傳輸協(xié)議名。};該結(jié)構(gòu)中s_proto所指向的傳輸協(xié)議名通常為TCP協(xié)議的協(xié)議名“TCP”或UDP協(xié)議的協(xié)議名稱“UDP”。2)getservbyport()該函數(shù)根據(jù)給定的協(xié)議和端口號(hào)查詢服務(wù)名稱等信息。structservent*getservbyport(intport,constchar*proto);函數(shù)參數(shù)port:

給定的端口號(hào),以網(wǎng)絡(luò)字節(jié)順序排列。proto:指向傳輸協(xié)議名的指針,可以為空指針。傳輸協(xié)議名通常為“TCP”或“UDP”。如果為空指針,函數(shù)返回第一個(gè)端口號(hào)與port匹配的服務(wù)條目。否則返回端口號(hào)與port以及傳輸協(xié)議名與proto都匹配的服務(wù)條目。返回值函數(shù)調(diào)用成功,將返回一個(gè)指向servent結(jié)構(gòu)的指針,該結(jié)構(gòu)由WinSock創(chuàng)建并管理,應(yīng)用程序不能修改或者釋放它的任何部分;函數(shù)調(diào)用不成功,將返回一個(gè)空指針NULL。例3.2顯示本機(jī)上所有的使用TCP協(xié)議的服務(wù)的名稱及端口號(hào)。#include"WinSock2.h"#include"iostream"#pragmacomment(lib,"ws2_32.lib")usingnamespacestd;intmain(intargc,char**argv){

structservent*pServer;WSADATAwsaData; if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)return0;

for(inti=1;i<65535;i++) { pServer=getservbyport(htons(i),"TCP");//獲取服務(wù)信息 if(pServer!=NULL) { cout<<"服務(wù)名:"<<pServer->s_name<<endl; cout<<"協(xié)議:"<<pServer->s_proto<<endl; cout<<"端口號(hào):"<<ntohs(pServer->s_port)<<endl; } } //注銷WinSockDLL WSACleanup(); return0;}3.6.3協(xié)議查詢因特網(wǎng)的每一個(gè)協(xié)議都有一個(gè)正式的名字,比如IP、ICMP、TCP等,但在網(wǎng)絡(luò)內(nèi)部,協(xié)議并不是使用它們的名字標(biāo)識(shí)的,而是使用分配給它們的一個(gè)唯一編號(hào)來表示的,比如IP的編號(hào)為0,ICMP的編號(hào)為1,TCP的編號(hào)為6等。因特網(wǎng)協(xié)議的編號(hào)是由IANA統(tǒng)一管理的。協(xié)議的名字主要便于人們閱讀,而協(xié)議的編號(hào)則有利于計(jì)算機(jī)處理。WinSock提供了兩個(gè)函數(shù)getprotobyname()和getprotobynumber()來幫助程序完成協(xié)議名字和編號(hào)之間的轉(zhuǎn)換。1)getprotobyname()根據(jù)協(xié)議的名字查詢相應(yīng)的協(xié)議編號(hào)等信息。structprotoent*getprotobyname(constchar*name);函數(shù)參數(shù)name:一個(gè)指向協(xié)議名的指針。返回值函數(shù)調(diào)用成功,將返回一個(gè)指向protoent結(jié)構(gòu)的指針,該protoent結(jié)構(gòu)由WinSock創(chuàng)建并管理,應(yīng)用程序不能修改或者釋放它的任何部分;函數(shù)調(diào)用失敗則返回一個(gè)空指針。protoent結(jié)構(gòu)的聲明如下:structprotoent{charp_name;//正規(guī)的協(xié)議名。char**p_aliases;//一個(gè)以空指針結(jié)尾的可選協(xié)議名隊(duì)列。shortp_proto;//主機(jī)字節(jié)順序的協(xié)議號(hào)。};getprotobynumber()根據(jù)協(xié)議號(hào)查詢協(xié)議的名字等信息。structprotoent*getprotobynumber(intnumber);函數(shù)參數(shù)number:一個(gè)以主機(jī)順序排列的協(xié)議號(hào)。返回值函數(shù)調(diào)用成功,將返回一個(gè)指向protoent結(jié)構(gòu)的指針,該protoent結(jié)構(gòu)由WinSock創(chuàng)建并管理,應(yīng)用程序不能修改或者釋放它的任何部分;函數(shù)調(diào)用失敗則返回一個(gè)空指針。例3.3顯示本機(jī)運(yùn)行的所有因特網(wǎng)協(xié)議的名稱及相應(yīng)的協(xié)議號(hào)。#include"iostream"#include"WinSock2.h"#pragmacomment(lib,"ws2_32.lib")usingnamespacestd;intmain(intargc,char**argv){WSADATAwsaData;if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0){cout<<"加載WinSockDLL失??!\n";return0;}structprotoent*pProto;for(inti=0;i<=255;i++){if((pProto=getprotobynumber(i))!=NULL)cout<<"協(xié)議名:"<<pProto->p_name<<"編號(hào):"<<pProto->p_proto<<endl;}WSACleanup();return0;}3.6.4異步信息查詢函數(shù)及其編程方法前面所介紹的函數(shù)都與標(biāo)準(zhǔn)的Berkeley套接字函數(shù)兼容,當(dāng)函數(shù)調(diào)用后,如果函數(shù)的功能沒有執(zhí)行完成,函數(shù)是不會(huì)返回的,緊跟在這些函數(shù)調(diào)用之后的語句在函數(shù)未返回前是無法執(zhí)行的。如果一個(gè)函數(shù)要完成其功能需要花費(fèi)較長時(shí)間,則應(yīng)用程序也只能在這個(gè)函數(shù)上等待而不能執(zhí)行其他操作(這種情況被稱為阻塞),一直到函數(shù)完成功能返回或者函數(shù)出錯(cuò)返回。套接字函數(shù)的這種工作模型被稱為同步模型。與同步模型所對(duì)應(yīng)的是異步模型,這里所謂的“異步”也就是指函數(shù)啟動(dòng)了規(guī)定的任務(wù)后立即返回調(diào)用方,并返回一個(gè)用來標(biāo)識(shí)所啟動(dòng)任務(wù)的異步任務(wù)句柄。為了適應(yīng)Windows的消息驅(qū)動(dòng)機(jī)制,同時(shí)也可以解決同步模型中那些耗時(shí)較多的套接字函數(shù)所引起的程序執(zhí)行效率低的問題,WinSock2引入Windows的消息機(jī)制對(duì)近20個(gè)套接字函數(shù)進(jìn)行了擴(kuò)展,就是所謂的異步擴(kuò)展,擴(kuò)展后的函數(shù)都以“WSA”開頭。在擴(kuò)展后的“異步”模型中,套接字函數(shù)通常只是啟動(dòng)一個(gè)任務(wù)并向Windows系統(tǒng)注冊(cè)一條消息后就立即返回,盡管所啟動(dòng)的任務(wù)還沒有完成。函數(shù)返回后應(yīng)用程序可以執(zhí)行其它操作。當(dāng)異步函數(shù)所啟動(dòng)的任務(wù)完成時(shí),Windows系統(tǒng)就會(huì)向應(yīng)用程序發(fā)送那條函數(shù)注冊(cè)的消息,收到消息后,應(yīng)用程序就可以根據(jù)任務(wù)完成的結(jié)果繼續(xù)下一步的操作。下面以gethostbyname()函數(shù)的異步擴(kuò)展版本W(wǎng)SAAsyncGetHostByName()函數(shù)為例,介紹WinSock2的異步函數(shù)的格式以及編程方法。WSAAsyncGetHostByName()函數(shù)的功能是根據(jù)主機(jī)域名獲取主機(jī)的IP地址等主機(jī)信息。函數(shù)被調(diào)用時(shí),如果給定的參數(shù)均有效,該函數(shù)將初始化并啟動(dòng)查詢操作后立即返回,返回值是異步任務(wù)句柄。特別說明WSAAsyncGetHostByName()函數(shù)現(xiàn)在已不被提倡使用,在VC++2017中使用該函數(shù)時(shí),編譯器將發(fā)出錯(cuò)誤警告并停止編譯,如果要關(guān)閉錯(cuò)誤警告繼續(xù)編譯,可以在頭文件stdafx.h中的頭部預(yù)處理命令#include"targetver.h"之下,添加如下宏定義:#define_WINSOCK_DEPRECATED_NO_WARNINGS或者在主對(duì)話框類的頭文件中,在類定義之前添加:#pragmawarning(disable:4996)WSAAsyncGetHostByName()這是gethostbyname()函數(shù)的異步擴(kuò)展版本。函數(shù)原型HANDLEWSAAsyncGetHostByName(HWNDhWnd,unsignedintwMsg,constcharFAR*name,constcharFAR*buf,intbuflen);由于該函數(shù)并不支持IPv6地址,同gethostbyname()一樣,該函數(shù)也已被建議停止使用,但其替代者GetAddrInfoExW()和GetAddrInfoExA()目前在Windows8之前的系統(tǒng)上都不支持異步編程模式。函數(shù)參數(shù)hWnd:是一個(gè)窗口句柄,表示異步請(qǐng)求完成時(shí)應(yīng)該收到本函數(shù)發(fā)出的消息的窗口。name:指向主機(jī)的名字的字符指針(字符串);wMsg:一般是用戶自定義的消息,表示函數(shù)結(jié)束時(shí)hWnd代表的窗口將要收到的消息。buf:接收hostent數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)區(qū)指針,該指針指向的數(shù)據(jù)區(qū)必須要大于hostent結(jié)構(gòu)的大小,這時(shí)因?yàn)樵摻Y(jié)構(gòu)不僅要容納hostent結(jié)構(gòu),而且hostent結(jié)構(gòu)的成員引用的所有數(shù)據(jù)也要保存在該存儲(chǔ)區(qū)內(nèi)。建議用戶提供一個(gè)MAXGETHOSTSTRUCT字節(jié)大小的緩沖區(qū)。buflen:buf所指緩沖區(qū)的大小。返回值該函數(shù)在執(zhí)行結(jié)束時(shí),如果異步操作成功執(zhí)行,則將主機(jī)名和地址信息復(fù)制到buf緩沖區(qū)中,同時(shí)向句柄為hWnd的應(yīng)用程序窗口發(fā)送消息wMsg。消息的wParam參數(shù)包含了函數(shù)調(diào)用時(shí)返回的異步任務(wù)句柄。lParam參數(shù)的高16位包含著錯(cuò)誤代碼,如果成功該錯(cuò)誤代碼為0,若該錯(cuò)誤代碼為WSAENOBUFS,則說明buflen指出的緩沖區(qū)大小太小了,此時(shí),lParma的低16位為實(shí)際所需緩沖區(qū)的大小。buf指向的數(shù)據(jù)區(qū)的最前面包含有一個(gè)hostent結(jié)構(gòu),通過該hostent結(jié)構(gòu)的成員就可以訪問網(wǎng)絡(luò)的IP地址等信息。因此,函數(shù)中先定義一個(gè)structhostent類型的指針hp

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(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ǔ)空間,僅對(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ì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論