基于delphi使用api實(shí)現(xiàn)sock通訊_第1頁(yè)
已閱讀1頁(yè),還剩31頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

1、<p>  基于Delphi使用API實(shí)現(xiàn)Sock通訊編程</p><p>  一、定址      要通過(guò)Winsock建立通信,必須了解如何利用指定的協(xié)議為工作站定址。Winsock 2引入了幾個(gè)新的、與協(xié)議無(wú)關(guān)的函數(shù),它們可和任何一個(gè)地址家族一起使用;但是大多數(shù)情況下,各協(xié)議家族都有自己的地址解析機(jī)制,要么通過(guò)一個(gè)函數(shù),要么作為一個(gè)投給getsoc

2、kopt的選項(xiàng)。     因?yàn)槟壳熬W(wǎng)絡(luò)編程中用的最多最普遍的也許就是TCP/IP協(xié)議了,所以這里主要介紹此協(xié)議下的WinSock編程。</p><p>  1、IP   網(wǎng)際協(xié)議(Internet Protocol, IP)是一種用于互聯(lián)網(wǎng)的網(wǎng)絡(luò)協(xié)議,已經(jīng)廣為人知。它可廣泛用于大多數(shù)計(jì)算機(jī)操作系統(tǒng)上,也可用于大多數(shù)局域網(wǎng)LAN(比如辦公室小型網(wǎng)絡(luò))和

3、廣域網(wǎng)WAN(比如說(shuō)互聯(lián)網(wǎng))。從它的設(shè)計(jì)看來(lái), IP是一個(gè)無(wú)連接的協(xié)議,不能保證數(shù)據(jù)投遞萬(wàn)無(wú)一失。兩個(gè)比它高級(jí)的協(xié)議(TCP和UDP)用于依賴IP協(xié)議的數(shù)據(jù)通信。2、TCP       面向連接的通信是通過(guò)“傳輸控制協(xié)議”(Transmission Control Protocol, TCP)來(lái)完成的。TCP提供兩臺(tái)計(jì)算機(jī)之間的可靠無(wú)錯(cuò)的數(shù)據(jù)傳輸。應(yīng)用程序利用TCP進(jìn)行通

4、信時(shí),源和目標(biāo)之間會(huì)建立一個(gè)虛擬連接。這個(gè)連接一旦建立,兩臺(tái)計(jì)算機(jī)之間就可以把數(shù)據(jù)當(dāng)作一個(gè)雙向字節(jié)流進(jìn)行交換。3、UDP       無(wú)連接通信是通過(guò)“用戶數(shù)據(jù)報(bào)協(xié)議”(User Datagram Protocol, UDP)來(lái)完成的。UDP不保障可靠數(shù)據(jù)的傳輸,但能夠向若干個(gè)目標(biāo)發(fā)送數(shù)據(jù),接收發(fā)自若干個(gè)源的數(shù)據(jù)。簡(jiǎn)單地說(shuō),如果一個(gè)客戶機(jī)向服務(wù)器發(fā)送數(shù)據(jù),這一數(shù)據(jù)會(huì)立即發(fā)

5、出,不管服務(wù)器是否已準(zhǔn)備接收數(shù)據(jù)。如果服務(wù)</p><p>  準(zhǔn)備使用哪個(gè)TCP或UDP通信端口來(lái)標(biāo)識(shí)服務(wù)器服務(wù)這一問(wèn)題,則由sin_port字段定義。在選擇端口時(shí),應(yīng)用必須特別小心,因?yàn)橛行┛捎枚丝谔?hào)是為“已知的”(即固定的)服務(wù)保留的(比如說(shuō)文件傳輸協(xié)議和超文本傳輸協(xié)議,即FTP和HTTP)?!耙阎膮f(xié)議”,即固定協(xié)議,采用的端口由“互聯(lián)網(wǎng)編號(hào)分配認(rèn)證(IANA)”控制和分配,RFC 1700中說(shuō)明編號(hào)。從

6、本質(zhì)上說(shuō),端口號(hào)分為下面這三類:“已知”端口、已注冊(cè)端口、動(dòng)態(tài)和(或)私用端口?!?0~1023由IANA控制,是為固定服務(wù)保留的?!?1024 ~ 49151是IANA列出來(lái)的、已注冊(cè)的端口,供普通用戶的普通用戶進(jìn)程或程序使用?!?49152 ~ 65535是動(dòng)態(tài)和(或)私用端口。   普通用戶應(yīng)用應(yīng)該選擇1024 ~ 49151之間的已注冊(cè)端口,從而避免端口號(hào)已被另一個(gè)應(yīng)用或系統(tǒng)服務(wù)所用。此外, 4915

7、2 ~ 65535之間的端口可自由使用,因?yàn)镮ANA這些端口上沒(méi)有注冊(cè)服務(wù)。在使用bind API函數(shù)時(shí),如果一個(gè)應(yīng)用和主機(jī)上的另一個(gè)應(yīng)用采用的端口號(hào)綁定在一起,系統(tǒng)就會(huì)返回Winsock錯(cuò)誤WSAEADDRINUSE。sock</p><p>  1. 特殊地址        對(duì)于特定情況下的套接字行為,有兩個(gè)特殊IP地址可對(duì)它們產(chǎn)生影響。

8、特殊地址INADDR_ANY允許服務(wù)器應(yīng)用監(jiān)聽(tīng)主機(jī)計(jì)算機(jī)上面每個(gè)網(wǎng)絡(luò)接口上的客戶機(jī)活動(dòng)。一般情況下,在該地址綁定套接字和本地接口時(shí),網(wǎng)絡(luò)應(yīng)用才利用這個(gè)地址來(lái)監(jiān)聽(tīng)連接。如果你有一個(gè)多址系統(tǒng),這個(gè)地址就允許一個(gè)獨(dú)立應(yīng)用接受發(fā)自多個(gè)接口的回應(yīng)。       特殊地址INADDR_BROADCAST用于在一個(gè)IP網(wǎng)絡(luò)中發(fā)送廣播UDP數(shù)據(jù)報(bào)。要使用這個(gè)特殊地址,需要應(yīng)用設(shè)置套接字選項(xiàng)S

9、O_BROADCAST。2. 字節(jié)排序   針對(duì)“大頭”(big-endian)和“小頭”(little-endian)形式的編號(hào),不同的計(jì)算機(jī)處理器的表示方法有所不同,這由各自的設(shè)計(jì)決定。比如, Intel 86處理器上,用“小頭”形式來(lái)表示多字節(jié)編號(hào):字節(jié)的排序是從最無(wú)意義的字節(jié)到最有意義的字節(jié)。在計(jì)算機(jī)中把IP地址和端口號(hào)指定成多字節(jié)數(shù)時(shí),這個(gè)數(shù)就按“主機(jī)字節(jié)”(host-byte)順序來(lái)表示。但是,如果

10、在網(wǎng)絡(luò)上指定I P地址和端口號(hào),“互聯(lián)網(wǎng)聯(lián)網(wǎng)標(biāo)準(zhǔn)”指定多字節(jié)值必須用“大頭”形式來(lái)表示(從最有意義的字節(jié)</p><p>  Winsock的初始化       每個(gè)Winsock應(yīng)用都必須加載Winsock DLL的相應(yīng)版本。如果調(diào)用Winsock之前,沒(méi)有加載Winsock庫(kù),這個(gè)函數(shù)就會(huì)返回一個(gè)SOCKET_ERROR,錯(cuò)誤信息是WSANOTIN

11、ITIALISED。   加載Winsock庫(kù)是通過(guò)調(diào)用WSAStartup函數(shù)實(shí)現(xiàn)的。這個(gè)函數(shù)在DELPHI中的WinSock單元被定義如下:   function WSAStartup(wVersionRequired: word; var WSData: TWSAData): Integer; stdcall;   ScktComp中這樣使用了此函數(shù)</p&g

12、t;<p>  procedure Startup;var  ErrorCode: Integer;begin  ErrorCode := WSAStartup($0101, WSAData);  if ErrorCode <> 0 then 

13、;   raise ESocketError.CreateResFmt(@sWindowsSocketError,      [SysErrorMessage(ErrorCode), ErrorCode, 'WSAStartup']);end;</p><p>  錯(cuò)誤檢查和

14、控制       對(duì)編寫(xiě)成功的Winsock應(yīng)用程序而言,錯(cuò)誤檢查和控制是至關(guān)重要的。事實(shí)上,對(duì)Winsock函數(shù)來(lái)說(shuō),返回錯(cuò)誤是非常常見(jiàn)的。但是,多數(shù)情況下,這些錯(cuò)誤都是無(wú)關(guān)緊要的,通信仍可在套接字上進(jìn)行。盡管其返回的值并非一成不變,但不成功的Winsock調(diào)用返回的最常見(jiàn)的值是SOCKET_ERROR。在詳細(xì)介紹各個(gè)API調(diào)用時(shí),我們打算指出和各個(gè)錯(cuò)誤對(duì)應(yīng)的返回值。實(shí)際上

15、,SOCKET_ERROR常量是- 1。       如果調(diào)用一個(gè)Winsock函數(shù),錯(cuò)誤情況發(fā)生了,就可用WSAGetLastError函數(shù)來(lái)獲得一段代碼,這段代碼明確地表明發(fā)生的狀況。該函數(shù)的定義如下:function WSAGetLastError: Integer; stdcall;      發(fā)生錯(cuò)誤之后調(diào)用這個(gè)函

16、數(shù),就會(huì)返回所發(fā)生的特定錯(cuò)誤的完整代碼。</p><p>  針對(duì)TCP/IP的WinSock編程        因?yàn)門CP協(xié)議是一個(gè)面向連接的協(xié)議,它存在一個(gè)概念上的“服務(wù)器”端和“客戶端”,在編碼時(shí),要區(qū)分對(duì)待。1、服務(wù)器端的編程     “服務(wù)器”在某種概念上我們可以理解為一個(gè)進(jìn)程,它需要等待

17、任意數(shù)量的客戶機(jī)連接,以便為它們的請(qǐng)求提供服務(wù)。對(duì)服務(wù)器監(jiān)聽(tīng)的連接來(lái)說(shuō),它必須在一個(gè)已知的名字上。在TCP/IP中,這個(gè)名字就是本地接口的I P地址,加上一個(gè)端口編號(hào)。每種協(xié)議都有一套不同的定址方案,所以有一種不同的命名方法。在Winsock中,第一步是將指定協(xié)議的套接字綁定到它已知的名字上。這個(gè)過(guò)程是通過(guò)API調(diào)用bind來(lái)完成的。下一步是將套接字置為監(jiān)聽(tīng)模式。這時(shí),用API函數(shù)listen來(lái)完成的。最后,若一個(gè)客戶機(jī)試圖建立連接,服

18、務(wù)器必須通過(guò)accept或WSAAccept調(diào)用來(lái)接受連接。 1.socket   function socket(af, Struct, protocol: Integer): TSocket; stdcall;      在加載Winsock DLL的相應(yīng)版本之后,你要做的第一件事就是建立一個(gè)</p><p>  2. b

19、ind       一旦為某種特定協(xié)議創(chuàng)建了套接字,就必須將套接字綁定到一個(gè)已知地址。bind函數(shù)可將指定的套接字同一個(gè)已知地址綁定到一起。該函數(shù)聲明如下;function bind(s: TSocket; var addr: TSockAddr; namelen: Integer): Integer; stdcall;    &#

20、160;  其中第一個(gè)參數(shù)s代表我們希望在上面等待客戶連接的那個(gè)套接字第二個(gè)參數(shù)addr,針對(duì)自己打算使用的那個(gè)協(xié)議,必須把該參數(shù)填充一個(gè)地址緩沖區(qū),第三個(gè)參數(shù)是要傳遞的、由協(xié)議決定的地址的長(zhǎng)度。例如這樣一段代碼</p><p>  var  ErrorCode : integer;  SockAdd_In : TSoc

21、kAddrIn;  ...begin  ...  SockAdd_In.sin_family := PF_INET;  SockAdd_In.sin_port := htons(FPort);  SockAdd_In.sin_addr.S_addr := htonl(

22、INADDR_ANY);  ErrorCode := bind(FSock,SockAdd_In,sizeof(SockAdd_In));</p><p>  一旦出錯(cuò), bind就會(huì)返回SOCKET_ERROR。對(duì)bind 來(lái)說(shuō),最常見(jiàn)的錯(cuò)誤是WSAEADDRINUSE。如使用的是TCP/IP,那么WSAEADDRINUSE就表示另一個(gè)進(jìn)程已經(jīng)同本地IP接口和端口號(hào)綁定

23、到了一起,或者那個(gè)IP接口和端口號(hào)處于TIME_WAIT狀態(tài)。假如你針對(duì)一個(gè)套接字調(diào)用bind,但那個(gè)套接字已經(jīng)綁定,便會(huì)返回WSAEFFAULT錯(cuò)誤。</p><p>  3. listen       我們接下來(lái)要做的是將套接字置入監(jiān)聽(tīng)模式。bind函數(shù)的作用只是將一個(gè)套接字和一個(gè)指定的地址關(guān)聯(lián)在一起。指示一個(gè)套接字等候進(jìn)入連接的API函數(shù)則是li

24、sten,其定義如下:  function listen(s: TSocket; backlog: Integer): Integer; stdcall;       第一個(gè)參數(shù)同樣是限定套接字。backlog參數(shù)指定了正在等待連接的最大隊(duì)列長(zhǎng)度。這個(gè)參數(shù)非常重要,因?yàn)橥耆赡芡瑫r(shí)出現(xiàn)幾個(gè)服務(wù)器連接請(qǐng)求。例如,假定backlog參數(shù)為2。如果三個(gè)客戶機(jī)同時(shí)發(fā)出請(qǐng)求

25、,那么頭兩個(gè)會(huì)被放在一個(gè)“待決”(等待處理)隊(duì)列中,以便應(yīng)用程序依次為它們提供服務(wù)。而第三個(gè)連接會(huì)造成一個(gè)WSAECONNREFUSED錯(cuò)誤。注意,一旦服務(wù)器接受了一個(gè)連接,那個(gè)連接請(qǐng)求就會(huì)從隊(duì)列中刪去,以便別人可繼續(xù)發(fā)出請(qǐng)求。backlog參數(shù)其實(shí)本身就存在著限制,這個(gè)限制是由基層的協(xié)議提供者決定的。如果出現(xiàn)非法值,那么會(huì)用與之最接近的一個(gè)合法值來(lái)取代。除此以外,對(duì)于如何知道實(shí)際的backlog值,其實(shí)并不存在一種標(biāo)準(zhǔn)手段。與list

26、en對(duì)應(yīng)</p><p>  4. accept      現(xiàn)在,我們已做好了接受客戶連接的準(zhǔn)備。這是通過(guò)accept或WSAAccept函數(shù)來(lái)完成的。accept格式如下:   function accept(s: TSocket; addr: PSockAddr; addrlen: PInteger): TSocket; stdcal

27、l;      其中,參數(shù)s是一個(gè)限定套接字,它處在監(jiān)聽(tīng)模式。第二個(gè)參數(shù)應(yīng)該是一個(gè)有效的SOCKADDR_IN結(jié)構(gòu)的地址,而addrlen應(yīng)該是SOCKADDR_IN結(jié)構(gòu)的長(zhǎng)度。對(duì)于屬于另一種協(xié)議的套接字,應(yīng)當(dāng)用與那種協(xié)議對(duì)應(yīng)的SOCKADDR結(jié)構(gòu)來(lái)替換SOCKADDR_IN。通過(guò)對(duì)accpet函數(shù)的調(diào)用,可為待決連接隊(duì)列中的第一個(gè)連接請(qǐng)求提供服務(wù)。accept函數(shù)返回后,addr結(jié)構(gòu)

28、中會(huì)包含發(fā)出連接請(qǐng)求的那個(gè)客戶機(jī)的I P地址信息,而addrlen參數(shù)則指出結(jié)構(gòu)的長(zhǎng)度。此外,accept會(huì)返回一個(gè)新的套接字描述符,它對(duì)應(yīng)于已經(jīng)接受的那個(gè)客戶機(jī)連接。對(duì)于該客戶機(jī)后續(xù)的所有操作,都應(yīng)使用這個(gè)新套接字。至于原來(lái)那個(gè)監(jiān)聽(tīng)套接字,它仍然用于接受其他客戶機(jī)連接,而且仍處于監(jiān)聽(tīng)模式。</p><p>  2、客戶機(jī)API函數(shù)      

29、0; 客戶機(jī)要簡(jiǎn)單得多,建立成功連接所需的步驟也要少得多??蛻魴C(jī)只需三步操作:1) 用socket創(chuàng)建一個(gè)套接字。2) 解析服務(wù)器名(以基層協(xié)議為準(zhǔn))。3) 用connect初始化一個(gè)連接。</p><p>  connect函數(shù)     關(guān)于創(chuàng)建套接字和解析服務(wù)器名的方法,前面已有簡(jiǎn)單敘述,這里介紹最后一步連接的API函數(shù)。我們先來(lái)看看該函數(shù)的Winsock 1版

30、本,其定義如下:function connect(s: TSocket; var name: TSockAddr; namelen: Integer): Integer; stdcall;      該函數(shù)的參數(shù)是相當(dāng)清楚的: s是即將在其上面建立連接的那個(gè)有效TCP套接字; name是針對(duì)TCP(說(shuō)明連接的服務(wù)器)的套接字地址結(jié)構(gòu)(SOCKADDR_IN);namelen則是名字參

31、數(shù)的長(zhǎng)度。</p><p>  3、數(shù)據(jù)傳輸      收發(fā)數(shù)據(jù)是網(wǎng)絡(luò)編程的主題。要在已建立連接的套接字上接收數(shù)據(jù),在Winsock 1版本中,可用這個(gè)A P I函數(shù):int send (    SOCKET s,     const char FAR * buf, 

32、0;   int len,     int flags    );   delphi中聲明如下:   function send(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall;</p><p>  SOC

33、KET參數(shù)是已建立連接的套接字,將在這個(gè)套接字上發(fā)送數(shù)據(jù)。第二個(gè)參數(shù)buf,則是字符緩沖區(qū),區(qū)內(nèi)包含即將發(fā)送的數(shù)據(jù)。第三個(gè)參數(shù)len,指定即將發(fā)送的緩沖區(qū)內(nèi)的字符數(shù)。最后,flags可為0、MSG_DONTROUTE或MSG_OOB。另外, flags還可以是對(duì)那些標(biāo)志進(jìn)行按位“或運(yùn)算”的一個(gè)結(jié)果。MSG_DONTROUTE標(biāo)志要求傳送層不要將它發(fā)出的包路由出去。由基層的傳送決定是否實(shí)現(xiàn)這一請(qǐng)求(例如,若傳送協(xié)議不支持該選項(xiàng),這一請(qǐng)求就

34、會(huì)被忽略)。MSG_OOB標(biāo)志預(yù)示數(shù)據(jù)應(yīng)該被帶外發(fā)送。對(duì)返回?cái)?shù)據(jù)而言,send返回發(fā)送的字節(jié)數(shù);若發(fā)生錯(cuò)誤,就返回SOCKET_ERROR。常見(jiàn)的錯(cuò)誤是WSAECONNABORTED,這一錯(cuò)誤一般發(fā)生在虛擬回路由于超時(shí)或協(xié)議有錯(cuò)而中斷的時(shí)候。發(fā)生這種情況時(shí),應(yīng)該關(guān)閉這個(gè)套接字,因?yàn)樗荒茉儆昧?。遠(yuǎn)程主機(jī)上的應(yīng)用通過(guò)執(zhí)行強(qiáng)行關(guān)閉或意外中斷操作重新設(shè)置虛擬虛路時(shí),或遠(yuǎn)程主機(jī)重新啟動(dòng)時(shí),發(fā)生的則是WSAECONNRESET錯(cuò)誤。再次提醒大家注

35、意,發(fā)生這一錯(cuò)誤時(shí),應(yīng)該關(guān)閉這個(gè)套接字。最后一個(gè)常見(jiàn)錯(cuò)誤是WSAETIMEOUT,它發(fā)生在連接由于網(wǎng)絡(luò)故障或遠(yuǎn)程連</p><p>  4、流協(xié)議        由于大多面向連接的協(xié)議同時(shí)也是流式傳輸協(xié)議,所以,在此提一下流式協(xié)議。對(duì)于流套接字上收發(fā)數(shù)據(jù)所用的函數(shù),需要明白的是:它們不能保證對(duì)請(qǐng)求的數(shù)據(jù)量進(jìn)行讀取或?qū)懭搿1热缯f(shuō),一個(gè)2048字

36、節(jié)的字符緩沖,準(zhǔn)備用send函數(shù)來(lái)發(fā)送它。對(duì)send函數(shù)而言,可能會(huì)返回已發(fā)出的少于2048的字節(jié)。是因?yàn)閷?duì)每個(gè)收發(fā)數(shù)據(jù)的套接字來(lái)說(shuō),系統(tǒng)都為它們分配了相當(dāng)充足的緩沖區(qū)空間。在發(fā)送數(shù)據(jù)時(shí),內(nèi)部緩沖區(qū)會(huì)將數(shù)據(jù)一直保留到應(yīng)該將它發(fā)到線上為止。幾種常見(jiàn)的情況都可導(dǎo)致這一情形的發(fā)生。比方說(shuō),大量數(shù)據(jù)的傳輸可以令緩沖區(qū)快速填滿。同時(shí),對(duì)TCP/IP來(lái)說(shuō),還有一個(gè)窗口大小的問(wèn)題。接收端會(huì)對(duì)窗口大小進(jìn)行調(diào)節(jié),以指出它可以接收多少數(shù)據(jù)。如果有大量數(shù)據(jù)涌

37、入接收端,接收端就會(huì)將窗口大小設(shè)為0,為待發(fā)數(shù)據(jù)做好準(zhǔn)備。對(duì)發(fā)送端來(lái)說(shuō),這樣會(huì)強(qiáng)令它在收到一個(gè)新的大于0的窗口大小之前,不得再發(fā)數(shù)據(jù)。在使用send調(diào)用時(shí),緩沖區(qū)可能只能容納1024個(gè)字節(jié),這時(shí),便有必要再提取剩下的1024個(gè)字節(jié)。</p><p>  5、中斷連接      一旦完成任務(wù),就必須關(guān)掉連接,釋放關(guān)聯(lián)到那個(gè)套接字句柄的所有資源。要真正地釋放與一個(gè)開(kāi)

38、著的套接字句柄關(guān)聯(lián)的資源,執(zhí)行closesocket調(diào)用即可。但要明白這一點(diǎn),closesocket可能會(huì)帶來(lái)負(fù)面影響(和如何調(diào)用它有關(guān)),即可能會(huì)導(dǎo)致數(shù)據(jù)的丟失。鑒于此,應(yīng)該在調(diào)用closesocket函數(shù)之前,利用shutdown函數(shù)從容中斷連接。接下來(lái),我們來(lái)談?wù)勥@兩個(gè)API函數(shù)。1. shutdown  為了保證通信方能夠收到應(yīng)用發(fā)出的所有數(shù)據(jù),對(duì)一個(gè)編得好的應(yīng)用來(lái)說(shuō),應(yīng)該通知接收端“不再發(fā)送數(shù)據(jù)”。同樣,通信方也

39、應(yīng)該如此。這就是所謂的“從容關(guān)閉”方法,并由shutdown函數(shù)來(lái)執(zhí)行。shutdown的定義如下:int shutdown (    SOCKET s,     int how    );       how參數(shù)可以是下面的任何一個(gè)值: SD_RECEIVE、SD_S

40、END或SD_BOTH。如果是SD_RECEIVE,就表示不允許再調(diào)用接收函數(shù)。這對(duì)底部的協(xié)議層沒(méi)有影響。另外,對(duì)TCP套接字來(lái)說(shuō),不管數(shù)據(jù)在等候接收,還是數(shù)據(jù)接連到達(dá),都要重設(shè)</p><p>  應(yīng)用WinSock建立客戶機(jī)/服務(wù)器程序的活動(dòng)圖</p><p>  一個(gè)典型的客戶機(jī)/服務(wù)器模式的會(huì)話程序的順序圖</p><p>  I/O控制指令 &

41、#160;      一系列套接字I/O控制函數(shù)用于在套接字之上,控制I/O的行為,同時(shí)獲取與那個(gè)套接字上進(jìn)行的I/O操作有關(guān)的信息。其中,第一個(gè)函數(shù)是ioctlsocket,起源于Winsock 1規(guī)范,聲明如下:int ioctlsocket (    SOCKET s,     long cmd,  &#

42、160;  u_long FAR* argp    );          其中,參數(shù)s指定的是要在上面采取I/O操作的套接字描述符,而cmd是一個(gè)預(yù)定義的標(biāo)志,用于打算執(zhí)行的I/O控制命令。最后一個(gè)參數(shù)argp對(duì)應(yīng)的是一個(gè)指針,指向與命令密切相關(guān)的一個(gè)變量。描述好每個(gè)命令之后,再給出要求變量的類型。</p>

43、<p>  標(biāo)準(zhǔn)I/O控制命令1. FIONBIO       該命令可在套接字s上允許或禁止“非鎖定”(Nonblocking)模式。默認(rèn)情況下,所有套接字在創(chuàng)建好后,都會(huì)自動(dòng)進(jìn)入“鎖定”套接字。若隨FIONBIO這個(gè)I/O控制命令來(lái)調(diào)用ioctlsocket,那么應(yīng)設(shè)置argp,令其傳遞指向一個(gè)“無(wú)符號(hào)”(無(wú)正負(fù)號(hào))長(zhǎng)整數(shù)的指針;若打算啟用非鎖定模式,應(yīng)將那

44、個(gè)長(zhǎng)整數(shù)的值設(shè)為一個(gè)非零值。而若設(shè)為0值,意味著套接字進(jìn)入鎖定模式。        調(diào)用WSAAsyncSelect或WSAEventSelect函數(shù)的時(shí)候,會(huì)將套接字自動(dòng)設(shè)為非鎖定模式。調(diào)用了其中任何一個(gè)函數(shù)之后,再有任何將套接字設(shè)回鎖定模式的企圖,都會(huì)以失敗告終,并返回WSAEINVAL 錯(cuò)誤。要想將套接字改回鎖定模式,應(yīng)用程序首先必須禁止WSAAsyncSelect。具

45、體的做法是調(diào)用WSAAsyncSelect,同時(shí)令其lEvent參數(shù)等于0?;蛘哒{(diào)用WSAEventSelect,令lNetworkEvents參數(shù)等于0,從而禁止WSAEventSelect2. FIONREAD       該命令用于決定可從套接字上自動(dòng)讀</p><p>  套接字模式     

46、; Windows套接字在兩種模式下執(zhí)行I/O操作:鎖定和非鎖定(阻塞和非阻塞)。     在鎖定模式下,在I/O操作完成前,執(zhí)行操作的Winsock函數(shù)(比如send和recv)會(huì)一直等候下去,不會(huì)立即返回程序(將控制權(quán)交還給程序)。而在非鎖定模式下, Winsock函數(shù)無(wú)論如何都會(huì)立即返回。      對(duì)于處在鎖定模式的套接字,我們必須多加

47、留意,因?yàn)樵谝粋€(gè)鎖定套接字上調(diào)用任何一個(gè)Winsock API函數(shù),都會(huì)產(chǎn)生相同的后果—耗費(fèi)或長(zhǎng)或短的時(shí)間“等待”。大多數(shù)Winsock應(yīng)用都是遵照一種“生產(chǎn)者-消費(fèi)者”模型來(lái)編制的。在這種模型中,應(yīng)用程序需要讀?。ɑ?qū)懭耄┲付〝?shù)量的字節(jié),然后以它為基礎(chǔ)執(zhí)行一些計(jì)算。這種方式下的使用,一定要注意到阻塞作用產(chǎn)生的副作用,例如,我們編寫(xiě)了了一個(gè)“服務(wù)器端”的進(jìn)程,創(chuàng)建一個(gè)套接字,然后在主線程中用一個(gè)循環(huán)接受客戶端發(fā)起的連接請(qǐng)求,我們用到了A

48、CCEPT函數(shù),那么在阻塞模式下,當(dāng)沒(méi)有客戶端請(qǐng)求發(fā)送時(shí),調(diào)用accept函數(shù)的線程(這里是主線程)將一直阻塞下去,不會(huì)返回,這也就意味著你其他的并發(fā)操作無(wú)法執(zhí)行,例如你的程序帶有GUI界面</p><p>  非鎖定模式        除了鎖定模式,我們還可考慮采用非鎖定模式的套接字。盡管這種套接字在使用上存在著些許難度,但只要排除了這項(xiàng)困

49、難,它在功能上還是非常強(qiáng)大的。除具備鎖定套接字已有的各項(xiàng)優(yōu)點(diǎn)之外,還進(jìn)行了少許擴(kuò)充,功能更強(qiáng)。將一個(gè)套接字置為非鎖定模式之后, Winsock API調(diào)用會(huì)立即返回。大多數(shù)情況下,這些調(diào)用都會(huì)“失敗”,并返回一個(gè)WSAEWOULDBLOCK錯(cuò)誤。什么意思呢?它意味著請(qǐng)求的操作在調(diào)用期間沒(méi)有時(shí)間完成。舉個(gè)例子來(lái)說(shuō),假如在系統(tǒng)的輸入緩沖區(qū)中,尚不存在“待決”的數(shù)據(jù),那么recv(接收數(shù)據(jù))調(diào)用就會(huì)返回WSAEWOULDBLOCK錯(cuò)誤。通常,

50、我們需要重復(fù)調(diào)用同一個(gè)函數(shù),直至獲得一個(gè)成功返回代碼。       由于非鎖定調(diào)用會(huì)頻繁返回WSAEWOULDBLOCK錯(cuò)誤,所以在任何時(shí)候,都應(yīng)仔細(xì)檢查所有返回代碼,并作好“失敗”的準(zhǔn)備。許多程序員易犯的一個(gè)錯(cuò)誤便是連續(xù)不停地調(diào)用一個(gè)函數(shù),直到它返回成功的消息為止。       鎖定和非鎖定套接字模式都存在著優(yōu)點(diǎn)

51、和缺點(diǎn)。其中,從概念的角度說(shuō),鎖定套接字更易使用。但在應(yīng)付建立連接的多個(gè)套接字時(shí),或在數(shù)據(jù)的收發(fā)量不均,時(shí)</p><p>  一個(gè)例子:   為了闡述鎖定模式和非鎖定模式的區(qū)別,可以用下面這個(gè)例子來(lái)演示:</p><p>  ...{    作者:wudi_1982    聯(lián)系方式:

52、wudi_1982@hotmail.com    轉(zhuǎn)載請(qǐng)著名出處}unit Unit1;interfaceuses  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, &#

53、160;Dialogs,winsock, StdCtrls;type  TForm1 = class(TForm)    Button1: TButton;    Button2: TButton;    ckbxB: TChe

54、ckBox;    Memo1: TMemo;    procedure Button1Click(Sender: TObject);    procedure Button2Click(Sender: TObject);  private

55、    ...{ Private declarations }  public    ...{ Public declarations }  end;  TSo</p><p>  在上面的例子中,首先通過(guò)點(diǎn)

56、擊一個(gè)按鈕創(chuàng)建一個(gè)服務(wù)器,如果選擇的是阻塞模式,你可以發(fā)現(xiàn)程序就想“死”了一樣,這是阻塞作用產(chǎn)生的效果,因?yàn)樯厦胬诱{(diào)用accept函數(shù)的地方是在主線程中,而此時(shí)沒(méi)有客戶端發(fā)起連接,因此accept將無(wú)法返回,主線程被阻塞。這種情況下,你根本無(wú)法點(diǎn)擊那個(gè)用來(lái)創(chuàng)建客戶端并發(fā)送數(shù)據(jù)的按鈕。然后再此執(zhí)行程序,使用非阻塞模式,你會(huì)看到程序執(zhí)行成功,創(chuàng)建客戶端按鈕可以執(zhí)行。如果有興趣,最好在兩種模式下使用單步執(zhí)行,來(lái)看以下效果,主要是關(guān)產(chǎn)acce

57、pt函數(shù)執(zhí)行的情況。當(dāng)然,你還可以把用來(lái)接受客戶端請(qǐng)求的那段代碼封裝到一個(gè)線程中去做,例如上面例子的讀取線程。       如果你仔細(xì)關(guān)產(chǎn)上面代碼,你可以看到在讀取線程中,有一段被我注釋掉的程序,它的主體是select,它是干什么的呢?下面我們就進(jìn)入Winsock i/o模式。</p><p>  套接字I/O模型   

58、   共有五種類型的套接字I/O模型,可讓W(xué)insock應(yīng)用程序?qū)/O進(jìn)行管理,它們包括: select(選擇)、WSAAsyncSelect(異步選擇)、WSAEventSelect(事件選擇)、overlapped(重疊)以及completion port(完成端口)。因?yàn)楸疚牡某霭l(fā)點(diǎn)是DELPHI中的TServerSocket控件,基于此控件的實(shí)現(xiàn),在這里,我打算向大家解釋主要解釋select以及WSAAsy

59、ncSelectI/O模型。</p><p>  select模型      select(選擇)模型是Winsock中最常見(jiàn)的I/O模型。之所以稱其為“ select模型”,是由于它的“中心思想”便是利用select函數(shù),實(shí)現(xiàn)對(duì)I/O的管理!最初設(shè)計(jì)該模型時(shí),主要面向的是某些使用Unix操作系統(tǒng)的計(jì)算機(jī),它們采用的是Berkeley套接字方案。select模型

60、已集成到Winsock 1.1中,它使那些想避免在套接字調(diào)用過(guò)程中被無(wú)辜“鎖定”的應(yīng)用程序,采取一種有序的方式,同時(shí)進(jìn)行對(duì)多個(gè)套接字的管理。由于Winsock 1.1向后兼容于Berkeley套接字實(shí)施方案,所以假如有一個(gè)Berkeley套接字應(yīng)用使用了select函數(shù),那么從理論角度講,毋需對(duì)其進(jìn)行任何修改,便可正常運(yùn)行。       利用select函數(shù),我們判斷套接字上

61、是否存在數(shù)據(jù),或者能否向一個(gè)套接字寫(xiě)入數(shù)據(jù)。之所以要設(shè)計(jì)這個(gè)函數(shù),唯一的目的便是防止應(yīng)用程序在套接字處于鎖定模式中時(shí),在一次I / O綁定調(diào)用(如send或recv)過(guò)程中,被迫進(jìn)入“鎖定”狀態(tài);同時(shí)防止在套接字處于非鎖定模式中時(shí),產(chǎn)生WSAEWOULDBLOCK錯(cuò)誤。除非滿足事先用參數(shù)規(guī)定的條件,否則</p><p>  例如,假定我們想測(cè)試一個(gè)套接字是否“可讀”,必須將自己的套接字增添到readfds集合,再

62、等待select函數(shù)完成。select完成之后,必須判斷自己的套接字是否仍為readfds集合的一部分。若答案是肯定的,便表明該套接字“可讀”,可立即著手從它上面讀取數(shù)據(jù)。在三個(gè)參數(shù)中(readfds、writefds和exceptfds),任何兩個(gè)都可以是空值( NULL);但是,至少有一個(gè)不能為空值!在任何不為空的集合中,必須包含至少一個(gè)套接字句柄;否則, select函數(shù)便沒(méi)有任何東西可以等待。最后一個(gè)參數(shù)timeout對(duì)應(yīng)的是一

63、個(gè)指針,它指向一個(gè)timeval結(jié)構(gòu),用于決定select最多等待I/O操作完成多久的時(shí)間。如timeout是一個(gè)空指針,那么select調(diào)用會(huì)無(wú)限期地“鎖定”或停頓下去,直到至少有一個(gè)描述符符合指定的條件后結(jié)束。對(duì)timeval結(jié)構(gòu)的定義如下:  timeval = record    tv_sec: Longint;    tv_usec: Longin

64、t;  end;       其中,tv_sec字段以秒為單位指定等待時(shí)間;tv_usec字段則</p><p>  var  FdSet : TFDSet;  TimeVal : TTimeVal;  ...begin 

65、 //前面的代碼不變  while true do  begin    FD_ZERO(FdSet);    FD_SET(FSock,FdSet);    TimeVal.tv_sec := 0; 

66、0;  TimeVal.tv_usec := 500;    //使用select函數(shù)    if (select(0,@fdSet,nil,nil,@TimeVal) > 0) then    begin 

67、0;   AddSize := sizeof(Add);     AcceptSock := accept(FSock,@Add,@AddSize);     if AcceptSock <> INVALID_SOCKET then&#

68、160;    TSockReadThread.Create(AcceptSock,Memo1);    end;    Application.ProcessMessages;  end;end;</p><p>  WSAAsyncSelect  

69、;    Winsock提供了一個(gè)有用的異步I/O模型。利用這個(gè)模型,應(yīng)用程序可在一個(gè)套接字上,接收以Windows消息為基礎(chǔ)的網(wǎng)絡(luò)事件通知。具體的做法是在建好一個(gè)套接字后,調(diào)用WSAAsyncSelect函數(shù)。該模型最早出現(xiàn)于Winsock的1.1版本中,用于幫助應(yīng)用程序開(kāi)發(fā)者面向一些早期的16位Windows平臺(tái),適應(yīng)其“落后”的多任務(wù)消息環(huán)境。應(yīng)用程序仍可從這種模型中得到好處,特別是它們用一個(gè)標(biāo)準(zhǔn)的W

70、indows例程(常稱為“ winproc”),對(duì)窗口消息進(jìn)行管理的時(shí)候。</p><p>  消息通知        要想使用WSAAsyncSelect模型,程序必須具備一個(gè)窗口,然后有消息循環(huán)系統(tǒng),我們通常會(huì)自定義一個(gè)消息,然后調(diào)用WSAAsyncSelect函數(shù)將此消息投遞到制定的窗口句柄中。WSAAsyncSelect函數(shù)定義如下

71、:int WSAAsyncSelect (    SOCKET s,     HWND hWnd,     unsigned int wMsg,     long lEvent    );       其中, s參數(shù)指定的是我

72、們感興趣的那個(gè)套接字。hWnd參數(shù)指定的是一個(gè)窗口句柄,它對(duì)應(yīng)于網(wǎng)絡(luò)事件發(fā)生之后,想要收到通知消息的那個(gè)窗口或?qū)υ捒?。wMsg參數(shù)指定在發(fā)生網(wǎng)絡(luò)事件時(shí),打算接收的消息。該消息會(huì)投遞到由hWnd窗口句柄指定的那個(gè)窗口。最后一個(gè)參數(shù)是lEvent,它指定的是一個(gè)位掩碼,對(duì)應(yīng)于一系列網(wǎng)絡(luò)事件的組合,應(yīng)用程序感興趣的便是這一系列事件。大多數(shù)應(yīng)用程序通常感興趣的網(wǎng)絡(luò)事件類型包括: FD_READ、FD_WRITE、FD_ACCEPT、FD_CON

73、NECT和FD_CLOSE。當(dāng)然,到底使用FD_ACCEPT,還是使用</p><p>  事件類型        含義FD_READ 應(yīng)用程序想要接收有關(guān)是否可讀的通知,以便讀入數(shù)據(jù)FD_WRITE 應(yīng)用程序想要接收有關(guān)是否可寫(xiě)的通知,以便寫(xiě)入數(shù)據(jù)FD_OOB 應(yīng)用程序想接收是否有帶外( OOB)數(shù)據(jù)抵達(dá)的通知FD_ACCEPT 應(yīng)用

74、程序想接收與進(jìn)入連接有關(guān)的通知FD_CONNECT 應(yīng)用程序想接收與一次連接或者多點(diǎn)join操作完成的通知FD_CLOSE 應(yīng)用程序想接收與套接字關(guān)閉有關(guān)的通知FD_QOS 應(yīng)用程序想接收套接字“服務(wù)質(zhì)量”(QoS)發(fā)生更改的通知FD_GROUP_QOS 應(yīng)用程序想接收套接字組“服務(wù)質(zhì)量”發(fā)生更改的通知(現(xiàn)在沒(méi)什么用處,為未來(lái)套接字組的使用保留)FD_ROUTING_INTERFACE_CHANGE 應(yīng)用程序想接收在指定的方向

75、上,與路由接口發(fā)生變化的通知FD_ADDRESS_LIST_CHANGE應(yīng)用程序想接收針對(duì)套接字的協(xié)議家族,本地地址列表發(fā)生變化的通知       應(yīng)用程序在一個(gè)套接字上成功調(diào)用了WSAAsyncSelect 之后,應(yīng)用程序會(huì)在與hWnd窗口句柄參數(shù)對(duì)應(yīng)的窗口例程中,以Windows消息的形式,接收網(wǎng)</p><p><b>  一個(gè)例子:

76、</b></p><p>  ...{    作者:wudi_1982    聯(lián)系方式:wudi_1982@hotmail.com    轉(zhuǎn)載請(qǐng)著名出處}unit Unit1;interfaceuses  Windows, 

77、Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  Dialogs, StdCtrls,winsock;const  //自定義一個(gè)消息  WM_MySockMessage=wm_user + $0

78、101;type  TForm1 = class(TForm)    Button1: TButton;    ListBox1: TListBox;    mmSRec: TMemo;    B

79、utton2: TButton;    procedure Button1Click(Sender: TObject);    procedure Button2Click(Sender: TObject);  private    //消息

溫馨提示

  • 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ù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 眾賞文庫(kù)僅提供信息存儲(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)論