版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、<p> 基于Delphi使用API實現(xiàn)Sock通訊編程</p><p> 一、定址 要通過Winsock建立通信,必須了解如何利用指定的協(xié)議為工作站定址。Winsock 2引入了幾個新的、與協(xié)議無關(guān)的函數(shù),它們可和任何一個地址家族一起使用;但是大多數(shù)情況下,各協(xié)議家族都有自己的地址解析機制,要么通過一個函數(shù),要么作為一個投給getsoc
2、kopt的選項。 因為目前網(wǎng)絡編程中用的最多最普遍的也許就是TCP/IP協(xié)議了,所以這里主要介紹此協(xié)議下的WinSock編程。</p><p> 1、IP 網(wǎng)際協(xié)議(Internet Protocol, IP)是一種用于互聯(lián)網(wǎng)的網(wǎng)絡協(xié)議,已經(jīng)廣為人知。它可廣泛用于大多數(shù)計算機操作系統(tǒng)上,也可用于大多數(shù)局域網(wǎng)LAN(比如辦公室小型網(wǎng)絡)和
3、廣域網(wǎng)WAN(比如說互聯(lián)網(wǎng))。從它的設(shè)計看來, IP是一個無連接的協(xié)議,不能保證數(shù)據(jù)投遞萬無一失。兩個比它高級的協(xié)議(TCP和UDP)用于依賴IP協(xié)議的數(shù)據(jù)通信。2、TCP 面向連接的通信是通過“傳輸控制協(xié)議”(Transmission Control Protocol, TCP)來完成的。TCP提供兩臺計算機之間的可靠無錯的數(shù)據(jù)傳輸。應用程序利用TCP進行通
4、信時,源和目標之間會建立一個虛擬連接。這個連接一旦建立,兩臺計算機之間就可以把數(shù)據(jù)當作一個雙向字節(jié)流進行交換。3、UDP 無連接通信是通過“用戶數(shù)據(jù)報協(xié)議”(User Datagram Protocol, UDP)來完成的。UDP不保障可靠數(shù)據(jù)的傳輸,但能夠向若干個目標發(fā)送數(shù)據(jù),接收發(fā)自若干個源的數(shù)據(jù)。簡單地說,如果一個客戶機向服務器發(fā)送數(shù)據(jù),這一數(shù)據(jù)會立即發(fā)
5、出,不管服務器是否已準備接收數(shù)據(jù)。如果服務</p><p> 準備使用哪個TCP或UDP通信端口來標識服務器服務這一問題,則由sin_port字段定義。在選擇端口時,應用必須特別小心,因為有些可用端口號是為“已知的”(即固定的)服務保留的(比如說文件傳輸協(xié)議和超文本傳輸協(xié)議,即FTP和HTTP)?!耙阎膮f(xié)議”,即固定協(xié)議,采用的端口由“互聯(lián)網(wǎng)編號分配認證(IANA)”控制和分配,RFC 1700中說明編號。從
6、本質(zhì)上說,端口號分為下面這三類:“已知”端口、已注冊端口、動態(tài)和(或)私用端口?!?0~1023由IANA控制,是為固定服務保留的?!?1024 ~ 49151是IANA列出來的、已注冊的端口,供普通用戶的普通用戶進程或程序使用?!?49152 ~ 65535是動態(tài)和(或)私用端口。 普通用戶應用應該選擇1024 ~ 49151之間的已注冊端口,從而避免端口號已被另一個應用或系統(tǒng)服務所用。此外, 4915
7、2 ~ 65535之間的端口可自由使用,因為IANA這些端口上沒有注冊服務。在使用bind API函數(shù)時,如果一個應用和主機上的另一個應用采用的端口號綁定在一起,系統(tǒng)就會返回Winsock錯誤WSAEADDRINUSE。sock</p><p> 1. 特殊地址 對于特定情況下的套接字行為,有兩個特殊IP地址可對它們產(chǎn)生影響。
8、特殊地址INADDR_ANY允許服務器應用監(jiān)聽主機計算機上面每個網(wǎng)絡接口上的客戶機活動。一般情況下,在該地址綁定套接字和本地接口時,網(wǎng)絡應用才利用這個地址來監(jiān)聽連接。如果你有一個多址系統(tǒng),這個地址就允許一個獨立應用接受發(fā)自多個接口的回應。 特殊地址INADDR_BROADCAST用于在一個IP網(wǎng)絡中發(fā)送廣播UDP數(shù)據(jù)報。要使用這個特殊地址,需要應用設(shè)置套接字選項S
9、O_BROADCAST。2. 字節(jié)排序 針對“大頭”(big-endian)和“小頭”(little-endian)形式的編號,不同的計算機處理器的表示方法有所不同,這由各自的設(shè)計決定。比如, Intel 86處理器上,用“小頭”形式來表示多字節(jié)編號:字節(jié)的排序是從最無意義的字節(jié)到最有意義的字節(jié)。在計算機中把IP地址和端口號指定成多字節(jié)數(shù)時,這個數(shù)就按“主機字節(jié)”(host-byte)順序來表示。但是,如果
10、在網(wǎng)絡上指定I P地址和端口號,“互聯(lián)網(wǎng)聯(lián)網(wǎng)標準”指定多字節(jié)值必須用“大頭”形式來表示(從最有意義的字節(jié)</p><p> Winsock的初始化 每個Winsock應用都必須加載Winsock DLL的相應版本。如果調(diào)用Winsock之前,沒有加載Winsock庫,這個函數(shù)就會返回一個SOCKET_ERROR,錯誤信息是WSANOTIN
11、ITIALISED。 加載Winsock庫是通過調(diào)用WSAStartup函數(shù)實現(xiàn)的。這個函數(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> 錯誤檢查和
14、控制 對編寫成功的Winsock應用程序而言,錯誤檢查和控制是至關(guān)重要的。事實上,對Winsock函數(shù)來說,返回錯誤是非常常見的。但是,多數(shù)情況下,這些錯誤都是無關(guān)緊要的,通信仍可在套接字上進行。盡管其返回的值并非一成不變,但不成功的Winsock調(diào)用返回的最常見的值是SOCKET_ERROR。在詳細介紹各個API調(diào)用時,我們打算指出和各個錯誤對應的返回值。實際上
15、,SOCKET_ERROR常量是- 1。 如果調(diào)用一個Winsock函數(shù),錯誤情況發(fā)生了,就可用WSAGetLastError函數(shù)來獲得一段代碼,這段代碼明確地表明發(fā)生的狀況。該函數(shù)的定義如下:function WSAGetLastError: Integer; stdcall; 發(fā)生錯誤之后調(diào)用這個函
16、數(shù),就會返回所發(fā)生的特定錯誤的完整代碼。</p><p> 針對TCP/IP的WinSock編程 因為TCP協(xié)議是一個面向連接的協(xié)議,它存在一個概念上的“服務器”端和“客戶端”,在編碼時,要區(qū)分對待。1、服務器端的編程 “服務器”在某種概念上我們可以理解為一個進程,它需要等待
17、任意數(shù)量的客戶機連接,以便為它們的請求提供服務。對服務器監(jiān)聽的連接來說,它必須在一個已知的名字上。在TCP/IP中,這個名字就是本地接口的I P地址,加上一個端口編號。每種協(xié)議都有一套不同的定址方案,所以有一種不同的命名方法。在Winsock中,第一步是將指定協(xié)議的套接字綁定到它已知的名字上。這個過程是通過API調(diào)用bind來完成的。下一步是將套接字置為監(jiān)聽模式。這時,用API函數(shù)listen來完成的。最后,若一個客戶機試圖建立連接,服
18、務器必須通過accept或WSAAccept調(diào)用來接受連接。 1.socket function socket(af, Struct, protocol: Integer): TSocket; stdcall; 在加載Winsock DLL的相應版本之后,你要做的第一件事就是建立一個</p><p> 2. b
19、ind 一旦為某種特定協(xié)議創(chuàng)建了套接字,就必須將套接字綁定到一個已知地址。bind函數(shù)可將指定的套接字同一個已知地址綁定到一起。該函數(shù)聲明如下;function bind(s: TSocket; var addr: TSockAddr; namelen: Integer): Integer; stdcall;
20、160; 其中第一個參數(shù)s代表我們希望在上面等待客戶連接的那個套接字第二個參數(shù)addr,針對自己打算使用的那個協(xié)議,必須把該參數(shù)填充一個地址緩沖區(qū),第三個參數(shù)是要傳遞的、由協(xié)議決定的地址的長度。例如這樣一段代碼</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> 一旦出錯, bind就會返回SOCKET_ERROR。對bind 來說,最常見的錯誤是WSAEADDRINUSE。如使用的是TCP/IP,那么WSAEADDRINUSE就表示另一個進程已經(jīng)同本地IP接口和端口號綁定
23、到了一起,或者那個IP接口和端口號處于TIME_WAIT狀態(tài)。假如你針對一個套接字調(diào)用bind,但那個套接字已經(jīng)綁定,便會返回WSAEFFAULT錯誤。</p><p> 3. listen 我們接下來要做的是將套接字置入監(jiān)聽模式。bind函數(shù)的作用只是將一個套接字和一個指定的地址關(guān)聯(lián)在一起。指示一個套接字等候進入連接的API函數(shù)則是li
24、sten,其定義如下: function listen(s: TSocket; backlog: Integer): Integer; stdcall; 第一個參數(shù)同樣是限定套接字。backlog參數(shù)指定了正在等待連接的最大隊列長度。這個參數(shù)非常重要,因為完全可能同時出現(xiàn)幾個服務器連接請求。例如,假定backlog參數(shù)為2。如果三個客戶機同時發(fā)出請求
25、,那么頭兩個會被放在一個“待決”(等待處理)隊列中,以便應用程序依次為它們提供服務。而第三個連接會造成一個WSAECONNREFUSED錯誤。注意,一旦服務器接受了一個連接,那個連接請求就會從隊列中刪去,以便別人可繼續(xù)發(fā)出請求。backlog參數(shù)其實本身就存在著限制,這個限制是由基層的協(xié)議提供者決定的。如果出現(xiàn)非法值,那么會用與之最接近的一個合法值來取代。除此以外,對于如何知道實際的backlog值,其實并不存在一種標準手段。與list
26、en對應</p><p> 4. accept 現(xiàn)在,我們已做好了接受客戶連接的準備。這是通過accept或WSAAccept函數(shù)來完成的。accept格式如下: function accept(s: TSocket; addr: PSockAddr; addrlen: PInteger): TSocket; stdcal
27、l; 其中,參數(shù)s是一個限定套接字,它處在監(jiān)聽模式。第二個參數(shù)應該是一個有效的SOCKADDR_IN結(jié)構(gòu)的地址,而addrlen應該是SOCKADDR_IN結(jié)構(gòu)的長度。對于屬于另一種協(xié)議的套接字,應當用與那種協(xié)議對應的SOCKADDR結(jié)構(gòu)來替換SOCKADDR_IN。通過對accpet函數(shù)的調(diào)用,可為待決連接隊列中的第一個連接請求提供服務。accept函數(shù)返回后,addr結(jié)構(gòu)
28、中會包含發(fā)出連接請求的那個客戶機的I P地址信息,而addrlen參數(shù)則指出結(jié)構(gòu)的長度。此外,accept會返回一個新的套接字描述符,它對應于已經(jīng)接受的那個客戶機連接。對于該客戶機后續(xù)的所有操作,都應使用這個新套接字。至于原來那個監(jiān)聽套接字,它仍然用于接受其他客戶機連接,而且仍處于監(jiān)聽模式。</p><p> 2、客戶機API函數(shù)
29、0; 客戶機要簡單得多,建立成功連接所需的步驟也要少得多。客戶機只需三步操作:1) 用socket創(chuàng)建一個套接字。2) 解析服務器名(以基層協(xié)議為準)。3) 用connect初始化一個連接。</p><p> connect函數(shù) 關(guān)于創(chuàng)建套接字和解析服務器名的方法,前面已有簡單敘述,這里介紹最后一步連接的API函數(shù)。我們先來看看該函數(shù)的Winsock 1版
30、本,其定義如下:function connect(s: TSocket; var name: TSockAddr; namelen: Integer): Integer; stdcall; 該函數(shù)的參數(shù)是相當清楚的: s是即將在其上面建立連接的那個有效TCP套接字; name是針對TCP(說明連接的服務器)的套接字地址結(jié)構(gòu)(SOCKADDR_IN);namelen則是名字參
31、數(shù)的長度。</p><p> 3、數(shù)據(jù)傳輸 收發(fā)數(shù)據(jù)是網(wǎng)絡編程的主題。要在已建立連接的套接字上接收數(shù)據(jù),在Winsock 1版本中,可用這個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ù)是已建立連接的套接字,將在這個套接字上發(fā)送數(shù)據(jù)。第二個參數(shù)buf,則是字符緩沖區(qū),區(qū)內(nèi)包含即將發(fā)送的數(shù)據(jù)。第三個參數(shù)len,指定即將發(fā)送的緩沖區(qū)內(nèi)的字符數(shù)。最后,flags可為0、MSG_DONTROUTE或MSG_OOB。另外, flags還可以是對那些標志進行按位“或運算”的一個結(jié)果。MSG_DONTROUTE標志要求傳送層不要將它發(fā)出的包路由出去。由基層的傳送決定是否實現(xiàn)這一請求(例如,若傳送協(xié)議不支持該選項,這一請求就
34、會被忽略)。MSG_OOB標志預示數(shù)據(jù)應該被帶外發(fā)送。對返回數(shù)據(jù)而言,send返回發(fā)送的字節(jié)數(shù);若發(fā)生錯誤,就返回SOCKET_ERROR。常見的錯誤是WSAECONNABORTED,這一錯誤一般發(fā)生在虛擬回路由于超時或協(xié)議有錯而中斷的時候。發(fā)生這種情況時,應該關(guān)閉這個套接字,因為它不能再用了。遠程主機上的應用通過執(zhí)行強行關(guān)閉或意外中斷操作重新設(shè)置虛擬虛路時,或遠程主機重新啟動時,發(fā)生的則是WSAECONNRESET錯誤。再次提醒大家注
35、意,發(fā)生這一錯誤時,應該關(guān)閉這個套接字。最后一個常見錯誤是WSAETIMEOUT,它發(fā)生在連接由于網(wǎng)絡故障或遠程連</p><p> 4、流協(xié)議 由于大多面向連接的協(xié)議同時也是流式傳輸協(xié)議,所以,在此提一下流式協(xié)議。對于流套接字上收發(fā)數(shù)據(jù)所用的函數(shù),需要明白的是:它們不能保證對請求的數(shù)據(jù)量進行讀取或?qū)懭搿1热缯f,一個2048字
36、節(jié)的字符緩沖,準備用send函數(shù)來發(fā)送它。對send函數(shù)而言,可能會返回已發(fā)出的少于2048的字節(jié)。是因為對每個收發(fā)數(shù)據(jù)的套接字來說,系統(tǒng)都為它們分配了相當充足的緩沖區(qū)空間。在發(fā)送數(shù)據(jù)時,內(nèi)部緩沖區(qū)會將數(shù)據(jù)一直保留到應該將它發(fā)到線上為止。幾種常見的情況都可導致這一情形的發(fā)生。比方說,大量數(shù)據(jù)的傳輸可以令緩沖區(qū)快速填滿。同時,對TCP/IP來說,還有一個窗口大小的問題。接收端會對窗口大小進行調(diào)節(jié),以指出它可以接收多少數(shù)據(jù)。如果有大量數(shù)據(jù)涌
37、入接收端,接收端就會將窗口大小設(shè)為0,為待發(fā)數(shù)據(jù)做好準備。對發(fā)送端來說,這樣會強令它在收到一個新的大于0的窗口大小之前,不得再發(fā)數(shù)據(jù)。在使用send調(diào)用時,緩沖區(qū)可能只能容納1024個字節(jié),這時,便有必要再提取剩下的1024個字節(jié)。</p><p> 5、中斷連接 一旦完成任務,就必須關(guān)掉連接,釋放關(guān)聯(lián)到那個套接字句柄的所有資源。要真正地釋放與一個開
38、著的套接字句柄關(guān)聯(lián)的資源,執(zhí)行closesocket調(diào)用即可。但要明白這一點,closesocket可能會帶來負面影響(和如何調(diào)用它有關(guān)),即可能會導致數(shù)據(jù)的丟失。鑒于此,應該在調(diào)用closesocket函數(shù)之前,利用shutdown函數(shù)從容中斷連接。接下來,我們來談談這兩個API函數(shù)。1. shutdown 為了保證通信方能夠收到應用發(fā)出的所有數(shù)據(jù),對一個編得好的應用來說,應該通知接收端“不再發(fā)送數(shù)據(jù)”。同樣,通信方也
39、應該如此。這就是所謂的“從容關(guān)閉”方法,并由shutdown函數(shù)來執(zhí)行。shutdown的定義如下:int shutdown ( SOCKET s, int how ); how參數(shù)可以是下面的任何一個值: SD_RECEIVE、SD_S
40、END或SD_BOTH。如果是SD_RECEIVE,就表示不允許再調(diào)用接收函數(shù)。這對底部的協(xié)議層沒有影響。另外,對TCP套接字來說,不管數(shù)據(jù)在等候接收,還是數(shù)據(jù)接連到達,都要重設(shè)</p><p> 應用WinSock建立客戶機/服務器程序的活動圖</p><p> 一個典型的客戶機/服務器模式的會話程序的順序圖</p><p> I/O控制指令 &
41、#160; 一系列套接字I/O控制函數(shù)用于在套接字之上,控制I/O的行為,同時獲取與那個套接字上進行的I/O操作有關(guān)的信息。其中,第一個函數(shù)是ioctlsocket,起源于Winsock 1規(guī)范,聲明如下:int ioctlsocket ( SOCKET s, long cmd,
42、160; u_long FAR* argp ); 其中,參數(shù)s指定的是要在上面采取I/O操作的套接字描述符,而cmd是一個預定義的標志,用于打算執(zhí)行的I/O控制命令。最后一個參數(shù)argp對應的是一個指針,指向與命令密切相關(guān)的一個變量。描述好每個命令之后,再給出要求變量的類型。</p>
43、<p> 標準I/O控制命令1. FIONBIO 該命令可在套接字s上允許或禁止“非鎖定”(Nonblocking)模式。默認情況下,所有套接字在創(chuàng)建好后,都會自動進入“鎖定”套接字。若隨FIONBIO這個I/O控制命令來調(diào)用ioctlsocket,那么應設(shè)置argp,令其傳遞指向一個“無符號”(無正負號)長整數(shù)的指針;若打算啟用非鎖定模式,應將那
44、個長整數(shù)的值設(shè)為一個非零值。而若設(shè)為0值,意味著套接字進入鎖定模式。 調(diào)用WSAAsyncSelect或WSAEventSelect函數(shù)的時候,會將套接字自動設(shè)為非鎖定模式。調(diào)用了其中任何一個函數(shù)之后,再有任何將套接字設(shè)回鎖定模式的企圖,都會以失敗告終,并返回WSAEINVAL 錯誤。要想將套接字改回鎖定模式,應用程序首先必須禁止WSAAsyncSelect。具
45、體的做法是調(diào)用WSAAsyncSelect,同時令其lEvent參數(shù)等于0?;蛘哒{(diào)用WSAEventSelect,令lNetworkEvents參數(shù)等于0,從而禁止WSAEventSelect2. FIONREAD 該命令用于決定可從套接字上自動讀</p><p> 套接字模式
46、; Windows套接字在兩種模式下執(zhí)行I/O操作:鎖定和非鎖定(阻塞和非阻塞)。 在鎖定模式下,在I/O操作完成前,執(zhí)行操作的Winsock函數(shù)(比如send和recv)會一直等候下去,不會立即返回程序(將控制權(quán)交還給程序)。而在非鎖定模式下, Winsock函數(shù)無論如何都會立即返回。 對于處在鎖定模式的套接字,我們必須多加
47、留意,因為在一個鎖定套接字上調(diào)用任何一個Winsock API函數(shù),都會產(chǎn)生相同的后果—耗費或長或短的時間“等待”。大多數(shù)Winsock應用都是遵照一種“生產(chǎn)者-消費者”模型來編制的。在這種模型中,應用程序需要讀?。ɑ?qū)懭耄┲付〝?shù)量的字節(jié),然后以它為基礎(chǔ)執(zhí)行一些計算。這種方式下的使用,一定要注意到阻塞作用產(chǎn)生的副作用,例如,我們編寫了了一個“服務器端”的進程,創(chuàng)建一個套接字,然后在主線程中用一個循環(huán)接受客戶端發(fā)起的連接請求,我們用到了A
48、CCEPT函數(shù),那么在阻塞模式下,當沒有客戶端請求發(fā)送時,調(diào)用accept函數(shù)的線程(這里是主線程)將一直阻塞下去,不會返回,這也就意味著你其他的并發(fā)操作無法執(zhí)行,例如你的程序帶有GUI界面</p><p> 非鎖定模式 除了鎖定模式,我們還可考慮采用非鎖定模式的套接字。盡管這種套接字在使用上存在著些許難度,但只要排除了這項困
49、難,它在功能上還是非常強大的。除具備鎖定套接字已有的各項優(yōu)點之外,還進行了少許擴充,功能更強。將一個套接字置為非鎖定模式之后, Winsock API調(diào)用會立即返回。大多數(shù)情況下,這些調(diào)用都會“失敗”,并返回一個WSAEWOULDBLOCK錯誤。什么意思呢?它意味著請求的操作在調(diào)用期間沒有時間完成。舉個例子來說,假如在系統(tǒng)的輸入緩沖區(qū)中,尚不存在“待決”的數(shù)據(jù),那么recv(接收數(shù)據(jù))調(diào)用就會返回WSAEWOULDBLOCK錯誤。通常,
50、我們需要重復調(diào)用同一個函數(shù),直至獲得一個成功返回代碼。 由于非鎖定調(diào)用會頻繁返回WSAEWOULDBLOCK錯誤,所以在任何時候,都應仔細檢查所有返回代碼,并作好“失敗”的準備。許多程序員易犯的一個錯誤便是連續(xù)不停地調(diào)用一個函數(shù),直到它返回成功的消息為止。 鎖定和非鎖定套接字模式都存在著優(yōu)點
51、和缺點。其中,從概念的角度說,鎖定套接字更易使用。但在應付建立連接的多個套接字時,或在數(shù)據(jù)的收發(fā)量不均,時</p><p> 一個例子: 為了闡述鎖定模式和非鎖定模式的區(qū)別,可以用下面這個例子來演示:</p><p> ...{ 作者:wudi_1982 聯(lián)系方式:
52、wudi_1982@hotmail.com 轉(zhuǎn)載請著名出處}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> 在上面的例子中,首先通過點
56、擊一個按鈕創(chuàng)建一個服務器,如果選擇的是阻塞模式,你可以發(fā)現(xiàn)程序就想“死”了一樣,這是阻塞作用產(chǎn)生的效果,因為上面例子調(diào)用accept函數(shù)的地方是在主線程中,而此時沒有客戶端發(fā)起連接,因此accept將無法返回,主線程被阻塞。這種情況下,你根本無法點擊那個用來創(chuàng)建客戶端并發(fā)送數(shù)據(jù)的按鈕。然后再此執(zhí)行程序,使用非阻塞模式,你會看到程序執(zhí)行成功,創(chuàng)建客戶端按鈕可以執(zhí)行。如果有興趣,最好在兩種模式下使用單步執(zhí)行,來看以下效果,主要是關(guān)產(chǎn)acce
57、pt函數(shù)執(zhí)行的情況。當然,你還可以把用來接受客戶端請求的那段代碼封裝到一個線程中去做,例如上面例子的讀取線程。 如果你仔細關(guān)產(chǎn)上面代碼,你可以看到在讀取線程中,有一段被我注釋掉的程序,它的主體是select,它是干什么的呢?下面我們就進入Winsock i/o模式。</p><p> 套接字I/O模型
58、 共有五種類型的套接字I/O模型,可讓Winsock應用程序?qū)/O進行管理,它們包括: select(選擇)、WSAAsyncSelect(異步選擇)、WSAEventSelect(事件選擇)、overlapped(重疊)以及completion port(完成端口)。因為本文的出發(fā)點是DELPHI中的TServerSocket控件,基于此控件的實現(xiàn),在這里,我打算向大家解釋主要解釋select以及WSAAsy
59、ncSelectI/O模型。</p><p> select模型 select(選擇)模型是Winsock中最常見的I/O模型。之所以稱其為“ select模型”,是由于它的“中心思想”便是利用select函數(shù),實現(xiàn)對I/O的管理!最初設(shè)計該模型時,主要面向的是某些使用Unix操作系統(tǒng)的計算機,它們采用的是Berkeley套接字方案。select模型
60、已集成到Winsock 1.1中,它使那些想避免在套接字調(diào)用過程中被無辜“鎖定”的應用程序,采取一種有序的方式,同時進行對多個套接字的管理。由于Winsock 1.1向后兼容于Berkeley套接字實施方案,所以假如有一個Berkeley套接字應用使用了select函數(shù),那么從理論角度講,毋需對其進行任何修改,便可正常運行。 利用select函數(shù),我們判斷套接字上
61、是否存在數(shù)據(jù),或者能否向一個套接字寫入數(shù)據(jù)。之所以要設(shè)計這個函數(shù),唯一的目的便是防止應用程序在套接字處于鎖定模式中時,在一次I / O綁定調(diào)用(如send或recv)過程中,被迫進入“鎖定”狀態(tài);同時防止在套接字處于非鎖定模式中時,產(chǎn)生WSAEWOULDBLOCK錯誤。除非滿足事先用參數(shù)規(guī)定的條件,否則</p><p> 例如,假定我們想測試一個套接字是否“可讀”,必須將自己的套接字增添到readfds集合,再
62、等待select函數(shù)完成。select完成之后,必須判斷自己的套接字是否仍為readfds集合的一部分。若答案是肯定的,便表明該套接字“可讀”,可立即著手從它上面讀取數(shù)據(jù)。在三個參數(shù)中(readfds、writefds和exceptfds),任何兩個都可以是空值( NULL);但是,至少有一個不能為空值!在任何不為空的集合中,必須包含至少一個套接字句柄;否則, select函數(shù)便沒有任何東西可以等待。最后一個參數(shù)timeout對應的是一
63、個指針,它指向一個timeval結(jié)構(gòu),用于決定select最多等待I/O操作完成多久的時間。如timeout是一個空指針,那么select調(diào)用會無限期地“鎖定”或停頓下去,直到至少有一個描述符符合指定的條件后結(jié)束。對timeval結(jié)構(gòu)的定義如下: timeval = record tv_sec: Longint; tv_usec: Longin
64、t; end; 其中,tv_sec字段以秒為單位指定等待時間;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提供了一個有用的異步I/O模型。利用這個模型,應用程序可在一個套接字上,接收以Windows消息為基礎(chǔ)的網(wǎng)絡事件通知。具體的做法是在建好一個套接字后,調(diào)用WSAAsyncSelect函數(shù)。該模型最早出現(xiàn)于Winsock的1.1版本中,用于幫助應用程序開發(fā)者面向一些早期的16位Windows平臺,適應其“落后”的多任務消息環(huán)境。應用程序仍可從這種模型中得到好處,特別是它們用一個標準的W
70、indows例程(常稱為“ winproc”),對窗口消息進行管理的時候。</p><p> 消息通知 要想使用WSAAsyncSelect模型,程序必須具備一個窗口,然后有消息循環(huán)系統(tǒng),我們通常會自定義一個消息,然后調(diào)用WSAAsyncSelect函數(shù)將此消息投遞到制定的窗口句柄中。WSAAsyncSelect函數(shù)定義如下
71、:int WSAAsyncSelect ( SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent ); 其中, s參數(shù)指定的是我
72、們感興趣的那個套接字。hWnd參數(shù)指定的是一個窗口句柄,它對應于網(wǎng)絡事件發(fā)生之后,想要收到通知消息的那個窗口或?qū)υ捒?。wMsg參數(shù)指定在發(fā)生網(wǎng)絡事件時,打算接收的消息。該消息會投遞到由hWnd窗口句柄指定的那個窗口。最后一個參數(shù)是lEvent,它指定的是一個位掩碼,對應于一系列網(wǎng)絡事件的組合,應用程序感興趣的便是這一系列事件。大多數(shù)應用程序通常感興趣的網(wǎng)絡事件類型包括: FD_READ、FD_WRITE、FD_ACCEPT、FD_CON
73、NECT和FD_CLOSE。當然,到底使用FD_ACCEPT,還是使用</p><p> 事件類型 含義FD_READ 應用程序想要接收有關(guān)是否可讀的通知,以便讀入數(shù)據(jù)FD_WRITE 應用程序想要接收有關(guān)是否可寫的通知,以便寫入數(shù)據(jù)FD_OOB 應用程序想接收是否有帶外( OOB)數(shù)據(jù)抵達的通知FD_ACCEPT 應用
74、程序想接收與進入連接有關(guān)的通知FD_CONNECT 應用程序想接收與一次連接或者多點join操作完成的通知FD_CLOSE 應用程序想接收與套接字關(guān)閉有關(guān)的通知FD_QOS 應用程序想接收套接字“服務質(zhì)量”(QoS)發(fā)生更改的通知FD_GROUP_QOS 應用程序想接收套接字組“服務質(zhì)量”發(fā)生更改的通知(現(xiàn)在沒什么用處,為未來套接字組的使用保留)FD_ROUTING_INTERFACE_CHANGE 應用程序想接收在指定的方向
75、上,與路由接口發(fā)生變化的通知FD_ADDRESS_LIST_CHANGE應用程序想接收針對套接字的協(xié)議家族,本地地址列表發(fā)生變化的通知 應用程序在一個套接字上成功調(diào)用了WSAAsyncSelect 之后,應用程序會在與hWnd窗口句柄參數(shù)對應的窗口例程中,以Windows消息的形式,接收網(wǎng)</p><p><b> 一個例子:
76、</b></p><p> ...{ 作者:wudi_1982 聯(lián)系方式:wudi_1982@hotmail.com 轉(zhuǎn)載請著名出處}unit Unit1;interfaceuses Windows,
77、Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls,winsock;const //自定義一個消息 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. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 眾賞文庫僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 基于Hadoop的API使用模式挖掘系統(tǒng)設(shè)計與實現(xiàn).pdf
- api會標使用記錄
- sock stering.pdf
- 基于OSSIE的波形開發(fā)及API設(shè)計實現(xiàn).pdf
- 基于Linux平臺的藍牙API設(shè)計與實現(xiàn).pdf
- sock stering.pdf
- 基于delphi的網(wǎng)絡考核系統(tǒng)設(shè)計與實現(xiàn).pdf
- 附錄a使用opentoolsapi的delphi擴展示例
- 基于CORBA中間件的Parlay API實現(xiàn)研究.pdf
- 基于UEFI技術(shù)的API性能分析設(shè)計與實現(xiàn).pdf
- 基于Delphi的產(chǎn)能管理系統(tǒng)設(shè)計與實現(xiàn).pdf
- 基于Delphi的倉庫管理系統(tǒng)設(shè)計與實現(xiàn).pdf
- 基于delphi的門診掛號收費系統(tǒng)的設(shè)計與實現(xiàn)
- 基于Delphi簡易智能監(jiān)控系統(tǒng)的設(shè)計與實現(xiàn).pdf
- 基于delphi的新生報名管理系統(tǒng)設(shè)計與實現(xiàn)
- 基于delphi7.0的超市管理系統(tǒng)設(shè)計與實現(xiàn)
- 基于頻繁API使用模式挖掘的軟件缺陷定位方法研究.pdf
- 使用世紀晟人臉api實現(xiàn)人臉識別(3d動態(tài)人臉識別方式)
- 基于delphi的學生成績管理系統(tǒng)設(shè)計與實現(xiàn)
- 基于API截獲技術(shù)的遠程監(jiān)測系統(tǒng)設(shè)計與實現(xiàn).pdf
評論
0/150
提交評論