Winsocket入門教程二:非阻塞式服務(wù)器和客戶端程序(TCP)_第1頁(yè)
Winsocket入門教程二:非阻塞式服務(wù)器和客戶端程序(TCP)_第2頁(yè)
Winsocket入門教程二:非阻塞式服務(wù)器和客戶端程序(TCP)_第3頁(yè)
Winsocket入門教程二:非阻塞式服務(wù)器和客戶端程序(TCP)_第4頁(yè)
Winsocket入門教程二:非阻塞式服務(wù)器和客戶端程序(TCP)_第5頁(yè)
已閱讀5頁(yè),還剩18頁(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、Winsocket入門教程二:非阻塞式服務(wù)器和客戶端程序(TCP) HYPERLINK JavaScript:d=document;t=d.selection?(d.selection.type!=None?d.selection.createRange().text:):(d.getSelection?d.getSelection():);void(saveit=window.open(/storeit.aspx?t=+escape(d.title)+&u=+escape(d.location.href)+&c=+escape(t),saveit,scrollbars=no,width=59

2、0,height=300,left=75,top=20,status=no,resizable=yes);saveit.focus(); o 收藏到我的網(wǎng)摘中,并分享給我的朋友 收藏 上次為大家介紹了阻塞式多線程服務(wù)端程序和阻塞式客戶端程序的設(shè)計(jì)方法,但是在上文的最后也提到過(guò),服務(wù)器程序會(huì)因?yàn)榻⑦B接和關(guān)閉連接而頻繁的創(chuàng)建和關(guān)閉線程會(huì)產(chǎn)生大量的內(nèi)存碎片,從而導(dǎo)致服務(wù)端程序不能保證長(zhǎng)時(shí)間的穩(wěn)定運(yùn)行。因此我在這里為大家介紹另外一種建立服務(wù)器和客戶端程序的方法,即建立非阻塞式的服務(wù)器和客戶端程序。 那什么是非阻塞呢?非阻塞是相對(duì)于阻塞而言,阻塞指的是在進(jìn)行一個(gè)操作的時(shí)候,如服務(wù)器接收客戶端的連接(a

3、ccept),服務(wù)器或者客戶端讀寫數(shù)據(jù)(read、write),如果該操作沒(méi)有執(zhí)行完成(成功或者失敗都算是執(zhí)行完成),則程序會(huì)一直阻塞在操作執(zhí)行的地方,直到該操作返回一個(gè)明確的結(jié)果。而非阻塞式程序則不一樣,非阻塞式程序會(huì)在產(chǎn)生阻塞操作的地方阻塞一定的時(shí)間(該時(shí)間可以由程序員自己設(shè)置)。如果操作沒(méi)有完成,在到達(dá)所設(shè)置的時(shí)間之后,無(wú)論該操作成功與否,都結(jié)束該操作而執(zhí)行程序下面的操作。 為了執(zhí)行非阻塞操作,我們?cè)趧?chuàng)建了一個(gè)套接口后,需要將套接口設(shè)置為非阻塞的套接口。為了將套接口設(shè)置成為非阻塞套接口,我們需要調(diào)用ioctlsocket函數(shù)將套接口設(shè)置為非阻塞的套接口。ioctlsocket函數(shù)的定義如

4、下: int ioctlsocket( SOCKET HYPERLINK http:/mk:MSITStoreE/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/MSDN/2001OCT/1033/WinSock.chm: s, long HYPERLINK http:/mk:MSITStoreE/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/MSDN/2001OCT/1033/WinSock.chm: cmd, u_long FAR * HYPERLINK http:/mk:MSITStoreE/%E5%BC%80%E5%8F%91%E5

5、%B7%A5%E5%85%B7/MSDN/2001OCT/1033/WinSock.chm: argp) 該函數(shù)的作用是控制套接口的I/O模式。 參數(shù)s表示要設(shè)置的套接口;參數(shù)cmd表示要對(duì)該套接口設(shè)置的命令,為了要將套接口設(shè)置成為非阻塞的,我們應(yīng)該填寫FIONBIO;argp表示填寫命令的值,如我們要將套接口設(shè)置成非阻塞的,我們需要將值設(shè)置成為1,如果我們要將套接口設(shè)置成為非阻塞狀態(tài)的話,我們將值設(shè)置成為0就是了。 為了進(jìn)行非阻塞的操作,我們需要在進(jìn)行操作之前調(diào)用select函數(shù),select函數(shù)的定義如下: int select(int HYPERLINK http:/mk:MSITSto

6、reE/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/MSDN/2001OCT/1033/WinSock.chm: nfds, fd_set FAR * HYPERLINK http:/mk:MSITStoreE/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/MSDN/2001OCT/1033/WinSock.chm: readfds, fd_set FAR * HYPERLINK http:/mk:MSITStoreE/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/MSDN/2001OCT/1033/WinS

7、ock.chm: writefds, fd_set FAR * HYPERLINK http:/mk:MSITStoreE/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/MSDN/2001OCT/1033/WinSock.chm: exceptfds, const struct timeval FAR * HYPERLINK http:/mk:MSITStoreE/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/MSDN/2001OCT/1033/WinSock.chm: timeout); 該函數(shù)設(shè)定一個(gè)或多個(gè)套接口的狀態(tài),并進(jìn)行必要的等

8、待,以便執(zhí)行異步I/0(非阻塞)操作。 參數(shù)nfds被忽略,該參數(shù)的作用僅僅是為了與伯克利套接口相兼容;參數(shù)readfds表示要檢測(cè)的可讀套接口的集合(該參數(shù)可選,可為設(shè)置為NULL);參數(shù)readfds表示要檢測(cè)的可寫套接口的集合(該參數(shù)可選,可為設(shè)置為NULL);參數(shù)exceptfds表示要檢測(cè)的套接口的錯(cuò)誤(該參數(shù)可選,可為設(shè)置為NULL);參數(shù)timeout表示執(zhí)行該函數(shù)時(shí)需要等待的時(shí)間,如果為NULL則表示阻塞操作,為0則表示立即返回。 下面讓我們來(lái)看看參數(shù)類型fd_set,fd_set表示套接字的集合。在使用select函數(shù)時(shí),我們需要將相應(yīng)的套接字加入到相應(yīng)的集合中。如果集合中的

9、套接字有信號(hào),select函數(shù)的返回值即為集合中有信號(hào)的套接字?jǐn)?shù)量。 我們用下面的幾個(gè)宏來(lái)操作fd_set集合。我們可以使用FD_SET(s, *set)將套接字s加入到集合set中;我們可以使用FD_CLR(s, *set)將套接字s移除出集合set;我們可以使用FD_ZERO(*set)將集合set清空;最后,我們可以使用FD_ISSET(s, *set)來(lái)判斷套接字s是否在集合中有信號(hào)。 接下來(lái)再讓我們來(lái)看看select函數(shù)的三個(gè)集合參數(shù)readfds、writefds以及exceptfds。 readfds表示可讀套接字的集合,可讀套接字在三種情況下有信號(hào)出現(xiàn):一、如果集合中有套接字處

10、于監(jiān)聽狀態(tài),并且該套接字上有來(lái)自客戶端的連接請(qǐng)求;二、如果集合中的套接字收到了send操作發(fā)送過(guò)來(lái)的數(shù)據(jù);三、如果集合中的套接字被關(guān)閉、重置或者中斷。 writefds表示可寫套接字的集合,可寫套接字在兩種情況下有信號(hào)出現(xiàn):一、集合中的套接字經(jīng)過(guò)connect操作后,連接成功;二、可以用send操作向集合中的套接字寫數(shù)據(jù)。 exceptfds表示錯(cuò)誤套接字的集合,錯(cuò)誤套接字在兩種情況下有信號(hào)出現(xiàn):一、集合中的套接字經(jīng)過(guò)connect操作后,連接失??;二、有帶外數(shù)據(jù)到來(lái)。 在我們了解了創(chuàng)建服務(wù)器和客戶端程序的基礎(chǔ)知識(shí)后,我們?cè)賮?lái)看看示例程序,以加深我們對(duì)知識(shí)的理解。 程序的運(yùn)行結(jié)果如下所示: 下

11、面是服務(wù)器程序的代碼: HYPERLINK /shining100/archive/2010/06/06/5651878.aspx view plain HYPERLINK /shining100/archive/2010/06/06/5651878.aspx copy to clipboard HYPERLINK /shining100/archive/2010/06/06/5651878.aspx print HYPERLINK /shining100/archive/2010/06/06/5651878.aspx ?#include #include #include #include

12、#pragmacomment(lib,ws2_32.lib) #defineASSERTassert usingstd:cin; usingstd:cout; usingstd:endl; usingstd:list; typedeflistSocketList; typedeflist:iteratorSocketListIterator; staticconstintc_iPort=10001; boolGraceClose(SOCKET*ps); intmain() intiRet=SOCKET_ERROR; /初始化Winsocket,所有Winsocket程序必須先使用WSAStar

13、tup進(jìn)行初始化 WSADATAdata; ZeroMemory(&data,sizeof(WSADATA); iRet=WSAStartup(MAKEWORD(2,0),&data); ASSERT(SOCKET_ERROR!=iRet); /建立服務(wù)端程序的監(jiān)聽套接字 SOCKETskListen=INVALID_SOCKET; skListen=socket(AF_INET,SOCK_STREAM,0); ASSERT(INVALID_SOCKET!=skListen); /初始化監(jiān)聽套接字地址信息 sockaddr_inadrServ;/表示網(wǎng)絡(luò)地址 ZeroMemory(&adrSe

14、rv,sizeof(sockaddr_in); adrServ.sin_family=AF_INET;/初始化地址格式,只能為AF_INET adrServ.sin_port=htons(c_iPort);/初始化端口,由于網(wǎng)絡(luò)字節(jié)順序和主機(jī)字節(jié)順序相反,所以必須使用htons將主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序 adrServ.sin_addr.s_addr=INADDR_ANY;/初始化IP,由于是服務(wù)器程序,所以可以將INADDR_ANY賦給該字段,表示任意的IP /綁定監(jiān)聽套接字到本地 iRet=bind(skListen,(sockaddr*)&adrServ,sizeof(sockad

15、dr_in); ASSERT(SOCKET_ERROR!=iRet); /使用監(jiān)聽套接字進(jìn)行監(jiān)聽 iRet=listen(skListen,FD_SETSIZE);/SOMAXCONN表示可以連接到該程序的最大連接數(shù) ASSERT(SOCKET_ERROR!=iRet); coutServerbeganlistening.0) sockaddr_inadrClt; intiLen=sizeof(sockaddr_in); ZeroMemory(&adrClt,iLen); SOCKETs=accept(skListen,(sockaddr*)&adrClt,&iLen); ASSERT(INV

16、ALID_SOCKET!=s); sl.push_back(s); coutServeracceptedaconnection.Thesocketiss0) for(SocketListIteratoriter=sl.begin();iter!=sl.end();+iter) /如果有數(shù)據(jù)可讀,則遍歷套接字列表中的所有套接字 /檢測(cè)出有數(shù)據(jù)可讀的套接字 iRet=FD_ISSET(*iter,&fsRead); if(iRet0) /讀取套接字上的數(shù)據(jù) constintc_iBufLen=512; charszBufc_iBufLen+1=0; intiRead=SOCKET_ERROR; i

17、Read=recv(*iter,szBuf,c_iBufLen,0); if(0=iRead)/讀取出現(xiàn)錯(cuò)誤或者對(duì)方關(guān)閉連接 iRead=0?coutConnectionshutdownatsocket*iterendl: coutConnectionrecverroratsocket*iterendl; iRet=GraceClose(&(*iter);/如果出錯(cuò)則關(guān)閉套接字 ASSERT(iRet); else szBufiRead=0; coutServerrecvedmessagefromsocket*iter:szBufendl; /創(chuàng)建可寫集合 FD_SETfsWrite; FD_

18、ZERO(&fsWrite); FD_SET(*iter,&fsWrite); /如果有數(shù)據(jù)可寫,則向客戶端發(fā)送數(shù)據(jù) iRet=select(1,NULL,&fsWrite,NULL,&tv); if(0iRet) intiWrite=SOCKET_ERROR; iWrite=send(*iter,szBuf,iRead,0); if(SOCKET_ERROR=iWrite) coutSendmessageerroratsocket*iter0); if(SOCKET_ERROR=iRet) returnfalse; /清理該套接字的資源 iRet=closesocket(*ps); if(S

19、OCKET_ERROR=iRet) returnfalse; *ps=INVALID_SOCKET; returntrue; HTMLCONTROL Forms.HTML:TextArea.1 服務(wù)器程序的重點(diǎn)是我們需要將接受自客戶端程序的套接字加入到一個(gè)鏈表中,以方便我們的管理。 HYPERLINK /shining100/archive/2010/06/06/5651878.aspx view plain HYPERLINK /shining100/archive/2010/06/06/5651878.aspx copy to clipboard HYPERLINK /shining100

20、/archive/2010/06/06/5651878.aspx print HYPERLINK /shining100/archive/2010/06/06/5651878.aspx ?FD_SET(skListen,&fsListen); iRet=select(1,&fsListen,NULL,NULL,&tv); if(iRet0) sockaddr_inadrClt; intiLen=sizeof(sockaddr_in); ZeroMemory(&adrClt,iLen); SOCKETs=accept(skListen,(sockaddr*)&adrClt,&iLen); ASS

21、ERT(INVALID_SOCKET!=s); sl.push_back(s); coutServeracceptedaconnection.Thesocketiss=iRead)/讀取出現(xiàn)錯(cuò)誤或者對(duì)方關(guān)閉連接 iRead=0?coutConnectionshutdownatsocket*iterendl: coutConnectionrecverroratsocket*iterendl; iRet=GraceClose(&(*iter);/如果出錯(cuò)則關(guān)閉套接字 ASSERT(iRet); HTMLCONTROL Forms.HTML:TextArea.1 HYPERLINK /shining

22、100/archive/2010/06/06/5651878.aspx view plain HYPERLINK /shining100/archive/2010/06/06/5651878.aspx copy to clipboard HYPERLINK /shining100/archive/2010/06/06/5651878.aspx print HYPERLINK /shining100/archive/2010/06/06/5651878.aspx ?sl.remove(INVALID_SOCKET);/刪除無(wú)效的套接字,套接字在關(guān)閉后被設(shè)置為無(wú)效 HTMLCONTROL Form

23、s.HTML:TextArea.1 接下來(lái)再讓我們來(lái)看看客戶端程序的代碼。 HYPERLINK /shining100/archive/2010/06/06/5651878.aspx view plain HYPERLINK /shining100/archive/2010/06/06/5651878.aspx copy to clipboard HYPERLINK /shining100/archive/2010/06/06/5651878.aspx print HYPERLINK /shining100/archive/2010/06/06/5651878.aspx ?#include #

24、include #include #pragmacomment(lib,ws2_32.lib) #defineASSERTassert usingstd:cin; usingstd:cout; usingstd:endl; staticconstcharc_szIP=; staticconstintc_iPort=10001; boolGraceClose(SOCKET*ps); intmain() intiRet=SOCKET_ERROR; /初始化Winsocket,所有Winsocket程序必須先使用WSAStartup進(jìn)行初始化 WSADATAdata; ZeroMemory(&dat

25、a,sizeof(WSADATA); iRet=WSAStartup(MAKEWORD(2,0),&data); ASSERT(SOCKET_ERROR!=iRet); /建立連接套接字 SOCKETskClient=INVALID_SOCKET; skClient=socket(AF_INET,SOCK_STREAM,0); ASSERT(INVALID_SOCKET!=skClient); /初始化連接套接字地址信息 sockaddr_inadrServ;/表示網(wǎng)絡(luò)地址 ZeroMemory(&adrServ,sizeof(sockaddr_in); adrServ.sin_family=

26、AF_INET;/初始化地址格式,只能為AF_INET adrServ.sin_port=htons(c_iPort);/初始化端口,由于網(wǎng)絡(luò)字節(jié)順序和主機(jī)字節(jié)順序相反,所以必須使用htons將主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序 adrServ.sin_addr.s_addr=inet_addr(c_szIP);/初始化IP,由于網(wǎng)絡(luò)字節(jié)順序和主機(jī)字節(jié)順序相反,所以必須使用inet_addr將主機(jī)字節(jié)順序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)順序 /將套接口從阻塞狀態(tài)設(shè)置到非阻塞狀態(tài) unsignedlongulEnable=1; iRet=ioctlsocket(skClient,FIONBIO,&ulEnable);

27、 ASSERT(SOCKET_ERROR!=iRet); fd_setfsWrite; TIMEVALtv; tv.tv_sec=1; tv.tv_usec=0; coutClientbegantoconnecttotheserver.endl; for(;) /使用非阻塞方式連接服務(wù)器,請(qǐng)注意connect操作的返回值總是為SOCKET_ERROR iRet=connect(skClient,(sockaddr*)&adrServ,sizeof(sockaddr_in); intiErrorNo=SOCKET_ERROR; intiLen=sizeof(int); /如果getsockopt

28、返回值不為0,則說(shuō)明有錯(cuò)誤出現(xiàn) if(SOCKET_ERROR=iRet&0!=getsockopt(skClient,SOL_SOCKET,SO_ERROR,(char*)&iErrorNo,&iLen) coutAnerrorhappenedonconnectingtoserver.TheerrornoisiErrorNo .Theprogramwillexitnow.endl; exit(-1); FD_ZERO(&fsWrite); FD_SET(skClient,&fsWrite); /如果集合fsWrite中的套接字有信號(hào),則說(shuō)明連接成功,此時(shí)iRet的返回值大于0 iRet=select(1,NULL,&fsWrite,NULL,&tv); if(0iRet) cout

溫馨提示

  • 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ù)網(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)論