




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
本文格式為Word版,下載可任意編輯——SocketCTCP阻塞非阻塞總結(jié)
gethostbyaddr、gethostbyname、gethostname、getprotolbyname
getprotolbynumber、getserverbyname、getservbyport。
第三類是Berkekleysocket例程的Windows專用的擴(kuò)展函數(shù),如gethostbyname對(duì)應(yīng)的WSAAsynGetHostByName(其他數(shù)據(jù)庫(kù)函數(shù)除了gethostname都有異步版本),select對(duì)應(yīng)的WSAAsynSelect,判斷是否阻塞的函數(shù)WSAIsBlocking,得到上一次WindsockAPI錯(cuò)誤信息的WSAGetLastError,等等。
從另外一個(gè)角度,這些函數(shù)又可以分為兩類,一是阻塞函數(shù),一是非阻塞函數(shù)。所謂阻塞函數(shù),是指其完成指定的任務(wù)之前不允許程序調(diào)用另一個(gè)函數(shù),在Windows下還會(huì)阻塞本線程消息的發(fā)送。所謂非阻塞函數(shù),是指操作啟動(dòng)之后,假使可以馬上得到結(jié)果就返回結(jié)果,否則返回表示結(jié)果需要等待的錯(cuò)誤信息,不等待任務(wù)完成函數(shù)就返回。
首先,異步函數(shù)是非阻塞函數(shù);
其次,獲取遠(yuǎn)地信息的數(shù)據(jù)庫(kù)函數(shù)是阻塞函數(shù)(因此,WinSock提供了其異步版本);
在Berkeleysocket函數(shù)部分中,不涉及網(wǎng)絡(luò)I/O、本地端工作的函數(shù)是非阻塞函數(shù);
在Berkeleysocket函數(shù)部分中,網(wǎng)絡(luò)I/O的函數(shù)是可阻塞函數(shù),也就是它們可以阻塞執(zhí)行,也可以不阻塞執(zhí)行。這些函數(shù)都使用了一個(gè)socket,假使它們使用的socket是阻塞的,則這些函數(shù)是阻塞函數(shù);假使它們使用的socket是非阻塞的,則這些函數(shù)是非阻塞函數(shù)。
創(chuàng)立一個(gè)socket時(shí),可以指定它是否阻塞。在缺省狀況下,Berkerley的Socket函數(shù)和WinSock都創(chuàng)立“阻塞〞的socket。阻塞socket通過(guò)使用select函數(shù)或者WSAAsynSelect函數(shù)在指定操作下變成非阻塞的。WSAAsyncSelect函數(shù)原型如下。
intWSAAsyncSelect(
SOCKETs,
HWNDhWnd,
u_intwMsg,
longlEvent);
其中,參數(shù)1指定了要操作的socket句柄;參數(shù)2指定了一個(gè)窗口句柄;參數(shù)3指定了一個(gè)消息,參數(shù)4指定了網(wǎng)絡(luò)事件,可以是多個(gè)事件的組合,如:
FD_READ準(zhǔn)備讀
FD_WRITE準(zhǔn)備寫
FD_OOB帶外數(shù)據(jù)到達(dá)
FD_ACCEPT收到連接
FD_CONNECT完成連接
FD_CLOSE關(guān)閉socket。
用OR操作組合這些事件值,如FD_READ|FD_WRITE
WSAAsyncSelect函數(shù)表示對(duì)sockets監(jiān)測(cè)lEvent指定的網(wǎng)絡(luò)事件,假使有事件發(fā)生,則給窗口hWnd發(fā)送消息wMsg。
假定應(yīng)用程序的一個(gè)sockets指定了監(jiān)測(cè)FD_READ事件,則在FD_READ事件上變成非阻塞的。當(dāng)read函數(shù)被調(diào)用時(shí),不管是否讀到數(shù)據(jù)都馬上返回,假使返回一個(gè)錯(cuò)誤信息表示還在等待,則在等待的數(shù)據(jù)到達(dá)后,消息wMsg發(fā)送給窗口hWnd,應(yīng)用程序處理該消息讀取網(wǎng)絡(luò)數(shù)據(jù)。
對(duì)于異步函數(shù)的調(diào)用,以類似的過(guò)程最終得到結(jié)果數(shù)據(jù)。以gethostbyname的異步版本的使用為例進(jìn)行說(shuō)明。該函數(shù)原型如下:
HANDLEWSAAsyncGetHostByName(
HWNDhWnd,
u_intwMsg,
constcharFAR*name,
charFAR*buf,
intbuflen);
在調(diào)用WSAAsyncGetHostByName啟動(dòng)操作時(shí),不僅指定主機(jī)名字name,還指定了一個(gè)窗口句柄hWnd,一個(gè)消息IDwMsg,一個(gè)緩沖區(qū)及其長(zhǎng)度。假使不能馬上得到主機(jī)地址,則返回一個(gè)錯(cuò)誤信息表示還在等待。當(dāng)要的數(shù)據(jù)到達(dá)時(shí),WinSockDLL給窗口hWnd發(fā)送消息wMsg告知得到了主機(jī)地址,窗口過(guò)程從指定的緩沖區(qū)buf得到主機(jī)地址。
使用異步函數(shù)或者非阻塞的socket,主要是為了不阻塞本線程的執(zhí)行。在多進(jìn)程或者多線程的狀況下,可以使用兩個(gè)線程通過(guò)同步手段來(lái)完成異步函數(shù)或者非阻塞函數(shù)的功能。
2.Socket的使用
WinSock以DLL的形式提供,在調(diào)用任何WinSockAPI之前,必需調(diào)用函數(shù)WSAStartup進(jìn)行初始化,最終,調(diào)用函數(shù)WSACleanUp作清理工作。
MFC使用函數(shù)AfxSocketInit包裝了函數(shù)WSAStartup,在WinSock應(yīng)用程序的初始化函數(shù)IninInstance中調(diào)用AfxSocketInit進(jìn)行初始化。程序不必調(diào)用WSACleanUp。
Socket是網(wǎng)絡(luò)通信過(guò)程中端點(diǎn)的抽象表示。Socket在實(shí)現(xiàn)中以句柄的形式被創(chuàng)立,包含了進(jìn)行網(wǎng)絡(luò)通信必需的五種信息:連接使用的協(xié)議,本地主機(jī)的IP地址,本地進(jìn)程的協(xié)議端口,遠(yuǎn)地主機(jī)的IP地址,遠(yuǎn)地進(jìn)程的協(xié)議端口。
要使用socket,首先必需創(chuàng)立一個(gè)socket;然后,按要求配置socket;接著,按要求通過(guò)socket接收和發(fā)送數(shù)據(jù);最終,程序關(guān)閉此socket。
為了創(chuàng)立socket,使用socket函數(shù)得到一個(gè)socket句柄:
socket_handle=socket(protocol_family.Socket_type,protocol);
其中:protocol_family指定socket使用的協(xié)議,取值PF_INET,表示Internet(TCP/IP)協(xié)議族;Socket_type指socket面向連接或者使用數(shù)據(jù)報(bào);第三個(gè)參數(shù)表示使用TCP或者UDP協(xié)議。
當(dāng)一個(gè)socket被創(chuàng)立時(shí),WinSock將為一個(gè)內(nèi)部結(jié)構(gòu)分派內(nèi)存,在此結(jié)構(gòu)中保存此socket的信息,到此,socket連接使用的協(xié)議已經(jīng)確定。
創(chuàng)立了socket之后,配置socket:
對(duì)于面向連接的客戶,WinSock自動(dòng)保存本地IP地址和選擇協(xié)議端口,但是必需使用connect函數(shù)配置遠(yuǎn)地IP地址和遠(yuǎn)地協(xié)議端口:
result=connect(socket_handle,remote_socket_address,address_length)
remote_socket_address是一個(gè)指向特定socket結(jié)構(gòu)的指針,該地址結(jié)構(gòu)為socket保存了地址族、協(xié)議端口、網(wǎng)絡(luò)主機(jī)地址。
面向連接的服務(wù)器則使用bind指定本地信息,使用listen和accept獲取遠(yuǎn)地信息。
使用數(shù)據(jù)報(bào)的客戶或者服務(wù)器使用bind給socket指定本地信息,在發(fā)送或者接收數(shù)據(jù)時(shí)指定遠(yuǎn)地信息。
bind給socket指定一個(gè)本地IP地址和協(xié)議端口,如下:
result=bind(socket_hndle,local_socket_address,address_length)
參數(shù)類型同connect。
函數(shù)listen監(jiān)聽bind指定的端口,假使有遠(yuǎn)地客戶請(qǐng)求連接,使用accept接收請(qǐng)求,創(chuàng)立一個(gè)新的socket,并保存信息。
socket_new=accept(socket_listen,socket_address,address_length)
在socket配置好之后,使用socket發(fā)送或者接收數(shù)據(jù):
面向連接的socket使用send發(fā)送數(shù)據(jù),recv接收數(shù)據(jù);
使用數(shù)據(jù)報(bào)的socket使用sendto發(fā)送數(shù)據(jù),recvfrom接收數(shù)據(jù)。
1.MFC對(duì)WinSocktAPI的封裝
MFC提供了兩個(gè)類CAsyncSocket和CSocket來(lái)封裝WinSockAPI,這給程序員提供了一個(gè)更簡(jiǎn)單的網(wǎng)絡(luò)編程接口。
CAsyncSocket在較低層次上封裝了WinSockAPI,缺省狀況下,使用該類創(chuàng)立的socket是非阻塞的socket,所有操作都會(huì)馬上返回,假使沒有得到結(jié)果,返回WSAEWOULDBLOCK,表示是一個(gè)阻塞操作。
CSocket建立在CAsyncSocket的基礎(chǔ)上,是CAsyncSocket的派生類。也就是缺省狀況下使用該類創(chuàng)立的socket是非阻塞的socket,但是CSocket的網(wǎng)絡(luò)I/O是阻塞的,它在完成任務(wù)之后才返回。CSocket的阻塞不是建立在“阻塞〞socket的基礎(chǔ)上,而是在“非阻塞〞socket上實(shí)現(xiàn)的阻塞操作,在阻塞期間,CSocket實(shí)現(xiàn)了本線程的消息循環(huán),因此,雖然是阻塞操作,但是并不影響消息循環(huán),即用戶依舊可以和程序交互。
1.CAsyncSocket
CAsyncSocket封裝了低層的WinSockAPI,其成員變量m_hSocket保存其對(duì)應(yīng)的socket句柄。使用CAsyncSocket的方法如下:
首先,在堆或者棧中構(gòu)造一個(gè)CAsyncSocket對(duì)象,例如:
CAsyncSocketsock;或者
CAsyncSocket*pSock=newCAsyncSocket;
其次,調(diào)用Create創(chuàng)立socket,例如:
使用缺省參數(shù)創(chuàng)立一個(gè)面向連接的socket
sock.Create()
指定參數(shù)參數(shù)創(chuàng)立一個(gè)使用數(shù)據(jù)報(bào)的socket,本地端口為30
pSocket.Create(30,SOCK_DGRM);
其三,假使是客戶程序,使用Connect連接到遠(yuǎn)地;假使是服務(wù)程序,使用Listen監(jiān)聽遠(yuǎn)地的連接請(qǐng)求。
其四,使用成員函數(shù)進(jìn)行網(wǎng)絡(luò)I/O。
最終,銷毀CAsyncSocket,析構(gòu)函數(shù)調(diào)用Close成員函數(shù)關(guān)閉socket。
下面,分析CAsyncSocket的幾個(gè)函數(shù),從中可以看到它是如何封裝低層的WinSockAPI,簡(jiǎn)化有關(guān)操作的;還可以看到它是如何實(shí)現(xiàn)非阻塞的socket和非阻塞操作。
2.socket對(duì)象的創(chuàng)立和捆綁
(1)Create函數(shù)
首先,探討Create函數(shù),分析socket句柄如何被創(chuàng)立并和CAsyncSocket對(duì)象關(guān)聯(lián)。Create的實(shí)現(xiàn)如下:
BOOLCAsyncSocket::Create(UINTnSocketPort,intnSocketType,
longlEvent,LPCTSTRlpszSocketAddress)
{
if(Socket(nSocketType,lEvent))
{
if(Bind(nSocketPort,lpszSocketAddress))
returnTRUE;
intnResult=GetLastError();
Close();
WSASetLastError(nResult);
}
returnFALSE;
}
其中:
參數(shù)1表示本socket的端口,缺省是0,假使要?jiǎng)?chuàng)立數(shù)據(jù)報(bào)的socket,則必需指定一個(gè)端口號(hào)。
參數(shù)2表示本socket的類型,缺省是SOCK_STREAM,表示面向連接類型。
參數(shù)3是屏蔽位,表示希望對(duì)本socket監(jiān)測(cè)的事件,缺省是FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE。
參數(shù)4表示本socket的IP地址字符串,缺省是NULL。
Create調(diào)用Socket函數(shù)創(chuàng)立一個(gè)socket,并把它捆綁在this所指對(duì)象上,監(jiān)測(cè)指定的網(wǎng)絡(luò)事件。參數(shù)2和3被傳遞給Socket函數(shù),假使希望創(chuàng)立數(shù)據(jù)報(bào)的socket,不要使用缺省參數(shù),指定參數(shù)2是SOCK_DGRM。
假使上一步驟成功,則調(diào)用bind給新的socket分派端口和IP地址。
(2)Socket函數(shù)
接著,分析Socket函數(shù),其實(shí)現(xiàn)如下:
BOOLCAsyncSocket::Socket(intnSocketType,longlEvent,
intnProtocolType,intnAddressFormat)
{
ASSERT(m_hSocket==INVALID_SOCKET);
m_hSocket=socket(nAddressFormat,nSocketType,nProtocolType);
if(m_hSocket!=INVALID_SOCKET)
{
CAsyncSocket::AttachHandle(m_hSocket,this,FALSE);
returnAsyncSelect(lEvent);
}
returnFALSE;
}
其中:
參數(shù)1表示Socket類型,缺省值是SOCK_STREAM。
參數(shù)2表示希望監(jiān)測(cè)的網(wǎng)絡(luò)事件,缺省值同Create,指定了全部事件。
參數(shù)3表示使用的協(xié)議,缺省是0。實(shí)際上,SOCK_STREAM類型的socket使用TCP協(xié)議,SOCK_DGRM的socket則使用UDP協(xié)議。
參數(shù)4表示地址族(地址格式),缺省值是PF_INET(等同于AF_INET)。對(duì)于TCP/IP來(lái)說(shuō),協(xié)議族和地址族是同值的。
在socket沒有被創(chuàng)立之前,成員變量m_hSocket是一個(gè)無(wú)效的socket句柄。Socket函數(shù)把協(xié)議族、socket類型、使用的協(xié)議等信息傳遞給WinSockAPI函數(shù)socket,創(chuàng)立一個(gè)socket。假使創(chuàng)立成功,則把它捆綁在this所指對(duì)象。
(3)捆綁(Attatch)
捆綁過(guò)程類似于其他Windows對(duì)象,將在模塊線程狀態(tài)的WinSock映射中添加一對(duì)新的映射:this所指對(duì)象和新創(chuàng)立的socket對(duì)象的映射。
另外,假使本模塊線程狀態(tài)的“socket窗口〞沒有創(chuàng)立,則創(chuàng)立一個(gè),該窗口
在異步操作時(shí)用來(lái)接收WinSock的通知消息,窗口句柄保存到模塊線程狀態(tài)的m_hSocketWindow變量中。函數(shù)AsyncSelect將指定該窗口為網(wǎng)絡(luò)事件消息的接收窗口。
函數(shù)AttachHandle的實(shí)現(xiàn)在此不列舉了。
(4)指定要監(jiān)測(cè)的網(wǎng)絡(luò)事件
在捆綁完成之后,調(diào)用AsyncSelect指定新創(chuàng)立的socket將監(jiān)測(cè)的網(wǎng)絡(luò)事件。AsyncSelect實(shí)現(xiàn)如下:
BOOLCAsyncSocket::AsyncSelect(longlEvent)
{
ASSERT(m_hSocket!=INVALID_SOCKET);
_AFX_SOCK_THREAD_STATE*pState=_afxSockThreadState;
ASSERT(pState->m_hSocketWindow!=NULL);
returnWSAAsyncSelect(m_hSocket,pState->m_hSocketWindow,
WM_SOCKET_NOTIFY,lEvent)!=SOCKET_ERROR;
}
函數(shù)參數(shù)lEvent表示希望監(jiān)視的網(wǎng)絡(luò)事件。
_afxSockThreadState得到的是當(dāng)前的模塊線程狀態(tài),m_hSocketWindow是本模塊在當(dāng)前線程的“socket窗口〞,指定監(jiān)視m_hSocket的網(wǎng)絡(luò)事件,如指定事件發(fā)生,給窗口m_hSocketWindow發(fā)送WM_SOCKET_NOTIFY消息。
被指定的網(wǎng)絡(luò)事件對(duì)應(yīng)的網(wǎng)絡(luò)I/O將是異步操作,是非阻塞操作。例如:指定FR_READ導(dǎo)致Receive是一個(gè)異步操作,假使不能馬上讀到數(shù)據(jù),則返回一個(gè)錯(cuò)誤WSAEWOULDBLOCK。在數(shù)據(jù)到達(dá)之后,WinSock通知窗口m_hSocketWindow,導(dǎo)致OnReceive被調(diào)用。
指定FR_WRITE導(dǎo)致Send是一個(gè)異步操作,即使數(shù)據(jù)沒有送出也返回一個(gè)錯(cuò)誤WSAEWOULDBLOCK。在數(shù)據(jù)可以發(fā)送之后,WinSock通知窗口m_hSocketWindow,導(dǎo)致OnSend被調(diào)用。
指定FR_CONNECT導(dǎo)致Connect是一個(gè)異步操作,還沒有連接上就返回錯(cuò)誤信息WSAEWOULDBLOCK,在連接完成之后,WinSock通知窗口m_hSocketWindow,導(dǎo)致OnConnect被調(diào)用。
對(duì)于其他網(wǎng)絡(luò)事件,就不一一解釋了。
所以,使用CAsyncSocket時(shí),假使使用Create缺省創(chuàng)立socket,則所有網(wǎng)絡(luò)I/O都是異步操作,進(jìn)行有關(guān)網(wǎng)絡(luò)I/O時(shí)則必需覆蓋以下的相關(guān)函數(shù):
OnAccept、OnClose、OnConnect、OnOutOfBandData、OnReceive、OnSend。
(5)Bind函數(shù)
經(jīng)過(guò)上述過(guò)程,socket創(chuàng)立完畢,下面,調(diào)用Bind函數(shù)給m_hSocket指定本地端口和IP地址。Bind的實(shí)現(xiàn)如下:
BOOLCAsyncSocket::Bind(UINTnSocketPort,LPCTSTRlpszSocketAddress)
{
USES_CONVERSION;
//使用WinSock的地址結(jié)構(gòu)構(gòu)造地址信息
SOCKADDR_INsockAddr;
memset(
//得到地址參數(shù)的值
LPSTRlpszAscii=T2A((LPTSTR)lpszSocketAddress);
//指定是Internet地址類型
sockAddr.sin_family=AF_INET;
if(lpszAscii==NULL)
//沒有指定地址,則自動(dòng)得到一個(gè)本地IP地址
//把32比特的數(shù)據(jù)從主機(jī)字節(jié)序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序
sockAddr.sin_addr.s_addr=htonl(INADDR_ANY);
else
{
//得到地址
DWORDlResult=inet_addr(lpszAscii);
if(lResult==INADDR_NONE)
{
WSASetLastError(WSAEINVAL);
returnFALSE;
}
sockAddr.sin_addr.s_addr=lResult;
}
//假使端口為0,則WinSock分派一個(gè)端口(1024—5000)
//把16比特的數(shù)據(jù)從主機(jī)字節(jié)序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序
sockAddr.sin_port=htons((u_short)nSocketPort);
//Bind調(diào)用WinSockAPI函數(shù)bind
returnBind((SOCKADDR*)
}
其中:函數(shù)參數(shù)1指定了端口;參數(shù)2指定了一個(gè)包含本地地址的字符串,缺省是NULL。
函數(shù)Bind首先使用結(jié)構(gòu)SOCKADDR_IN構(gòu)造地址信息。該結(jié)構(gòu)的域sin_family表示地址格式(TCP/IP同協(xié)議族),賦值為AF_INET(Internet地址格式);域
sin_port表示端口,假使參數(shù)1為0,則WinSock分派一個(gè)端口給它,范圍在1024和5000之間;域sin_addr是表示地址信息,它是一個(gè)聯(lián)合體,其中s_addr表示如下形式的字符串,“28.56.22.8〞。假使參數(shù)沒有指定地址,則WinSock自動(dòng)地得到本地IP地址(假使有幾個(gè)網(wǎng)卡,則使用其中一個(gè)的地址)。
(6)總結(jié)Create的過(guò)程
首先,調(diào)用socket函數(shù)創(chuàng)立一個(gè)socket;然后把創(chuàng)立的socket對(duì)象映射到CAsyncSocket對(duì)象(捆綁在一起),指定本socket要通知的網(wǎng)絡(luò)事件,并創(chuàng)立一個(gè)“socket窗口〞來(lái)接收網(wǎng)絡(luò)事件消息,最終,指定socket的本地信息。
下一步,是使用成員函數(shù)Connect連接遠(yuǎn)地主機(jī),配置socket的遠(yuǎn)地信息。函數(shù)Connect類似于Bind,把指定的遠(yuǎn)地地址轉(zhuǎn)換成SOCKADDR_IN對(duì)象表示的地址信息(包括網(wǎng)絡(luò)字節(jié)序的轉(zhuǎn)換),然后調(diào)用WinSock函數(shù)Connect連接遠(yuǎn)地主機(jī),配置socket的遠(yuǎn)地端口和遠(yuǎn)地IP地址。
3.異步網(wǎng)絡(luò)事件的處理
當(dāng)網(wǎng)絡(luò)事件發(fā)生時(shí),“socket窗口〞接收WM_SOCKET_NOTIFY消息,消息處理函數(shù)OnSocketNotify被調(diào)用?!皊ocket窗口〞的定義和消息處理是MFC實(shí)現(xiàn)的,這里不作詳細(xì)的探討。
OnSocketNotify回調(diào)CAsyncSocket的成員函數(shù)DoCallBack,DoCallBack調(diào)用事件處理函數(shù),如OnRead、OnWrite等。摘錄DoCallBack的一段代碼如下:
switch(WSAGETSELECTEVENT(lParam))
{
caseFD_READ:
{
DWORDnBytes;
//得到可以一次讀取的字節(jié)數(shù)
pSocket->IOCtl(FIONREAD,
if(nBytes!=0)
pSocket->OnReceive(nErrorCode);
}
break;
caseFD_WRITE:
pSocket->OnSend(nErrorCode);
break;
caseFD_OOB:
pSocket->OnOutOfBandData(nErrorCode);
break;
caseFD_ACCEPT:
pSocket->OnAccept(nErrorCode);
break;
caseFD_CONNECT:
pSocket->OnConnect(nErrorCode);
break;
caseFD_CLOSE:
pSocket->OnClose(nErrorCode);
break;
lParam是WM_SOCKET_NOFITY的消息參數(shù),OnSocketNotify傳遞給函數(shù)DoCallBack,表示通知事件。
函數(shù)IOCtl是CAsyncSocket的成員函數(shù),用來(lái)對(duì)socket的I/O進(jìn)行控制。這里的使用表示本次調(diào)用Receive函數(shù)至多可以讀nBytes個(gè)字節(jié)。
從上面的探討可以看出,從創(chuàng)立socket到網(wǎng)絡(luò)I/O,CAsyncSocket直接封裝了低層的WinSockAPI,簡(jiǎn)化了WinSock編程,實(shí)現(xiàn)了一個(gè)異步操作的界面。假使希望某個(gè)操作是阻塞操作,則在調(diào)用Create時(shí)不要指定該操作對(duì)應(yīng)的網(wǎng)
絡(luò)事件。例如,希望Connect和Send是阻塞操作,在任務(wù)完成之后才返回,則可以使用如下的語(yǔ)句:
pSocket->Create(0,SOCK_STREAM,
FR_WRITE|FR_OOB|FR_ACCEPT|FR_CLOSE);
這樣,在Connect和Send時(shí),假使是用戶界面線程的話,可能阻塞線程消息循環(huán)。所以,最好在工線程中使用阻塞操作。
2.CSocket
假使希望在用戶界面線程中使用阻塞socket,則可以使用CSocket。它在非阻塞socket基礎(chǔ)之上實(shí)現(xiàn)了阻塞操作,在阻塞期間實(shí)現(xiàn)了消息循環(huán)。
對(duì)于CSocket,處理網(wǎng)絡(luò)事件通知的函數(shù)OnAccept、OnClose、OnReceive依舊可以使用,OnConnect、OnSend在CSocket中永遠(yuǎn)不會(huì)被調(diào)用,另外OnOutOfBandData在CSocket中不勉勵(lì)使用。
CSocket對(duì)象在調(diào)用Connect、Send、Accept、Close、Receive等成員函數(shù)后,這些函數(shù)在完成任務(wù)之后(連接被建立、數(shù)據(jù)被發(fā)送、連接請(qǐng)求被接收、socket被關(guān)閉、數(shù)據(jù)被讀?。┲蟛艜?huì)返回。因此,Connect和Send不會(huì)導(dǎo)致OnConnect和OnSend被調(diào)用。假使覆蓋虛擬函數(shù)OnReceive、OnAccept、OnClose,不主動(dòng)調(diào)用Receive、Accept、Close,則在網(wǎng)絡(luò)事件到達(dá)之后導(dǎo)致對(duì)應(yīng)的虛擬函數(shù)被調(diào)用,虛擬函數(shù)的實(shí)現(xiàn)應(yīng)當(dāng)調(diào)用Receive、Accept、Close來(lái)完成操作。下面,就一個(gè)函數(shù)Receive來(lái)考察CSocket如何實(shí)現(xiàn)阻塞操作和消息循環(huán)的。
intCSocket::Receive(void*lpBuf,intnBufLen,intnFlags)
{
//m_pbBlocking是CSocket的成員變量,用來(lái)標(biāo)識(shí)當(dāng)前是否正在進(jìn)行
//阻塞操作。但不能同時(shí)進(jìn)行兩個(gè)阻塞操作。
if(m_pbBlocking!=NULL)
{
WSASetLastError(WSAEINPROGRESS);
returnFALSE;
}
//完成數(shù)據(jù)讀取
intnResult;
while((nResult=CAsyncSocket::Receive(lpBuf,nBufLen,nFlags))
==SOCKET_ERROR)
{
if(GetLastError()==WSAEWOULDBLOCK)
{
//進(jìn)入消息循環(huán),等待網(wǎng)絡(luò)事件FD_READ
if(!PumpMessages(FD_READ))
returnSOCKET_ERROR;
}
else
returnSOCKET_ERROR;
}
returnnResult;
}
其中:
參數(shù)1指定一個(gè)緩沖區(qū)保存讀取的數(shù)據(jù);參數(shù)2指定緩沖區(qū)的大??;參數(shù)3取值MSG_PEEK(數(shù)據(jù)拷貝到緩沖區(qū),但不從輸入隊(duì)列移走),或者M(jìn)SG_OOB(處理帶外數(shù)據(jù)),或者M(jìn)SG_PEEK|MSG_OOB。
Receive函數(shù)首先判斷當(dāng)前CSocket對(duì)象是否正在處理一個(gè)阻塞操作,假使是,則返回錯(cuò)誤WSAEINPROGRESS;否則,開始數(shù)據(jù)讀取的處理。
讀取數(shù)據(jù)時(shí),假使基類CAsyncSocket的Receive讀取到了數(shù)據(jù),則返回;否則,如
果返回一個(gè)錯(cuò)誤,而且錯(cuò)誤號(hào)是WSAEWOULDBLOCK,則表示操作阻塞,于是調(diào)用PumpMessage進(jìn)入消息循環(huán)等待數(shù)據(jù)到達(dá)(網(wǎng)絡(luò)事件FD_READ發(fā)生)。數(shù)據(jù)到達(dá)之后退出消息循環(huán),再次調(diào)用CAsyncSocket的Receive讀取數(shù)據(jù),直到?jīng)]有數(shù)據(jù)可讀為止。
PumpMessages是CSocket的成員函數(shù),它完成以下工作:
(1)設(shè)置m_pbBlocking,表示進(jìn)入阻塞操作。
(2)進(jìn)行消息循環(huán),假使有以下事件發(fā)生則退出消息循環(huán):收到指定定時(shí)器的定時(shí)事件消息WM_TIMER,退出循環(huán),返回TRUE;收到發(fā)送給本socket的消息WM_SOCKET_NOTIFY,網(wǎng)絡(luò)事件FD_CLOSE或者等待的網(wǎng)絡(luò)事件發(fā)生,退出循環(huán),返回TRUE;發(fā)送錯(cuò)誤或者收到WM_QUIT消息,退出循環(huán),返回FALSE;
(3)在消息循環(huán)中,把WM_SOCKET_DEAD消息和發(fā)送給其他socket的通知消息WM_SOCKET_NOFITY放進(jìn)模塊線程狀態(tài)的通知消息列表m_listSocketNotifications,在阻塞操作完成之后處理;對(duì)其他消息,則把它們送給目的窗口的窗口過(guò)程處理。
3.CSocketFile
MFC還提供了一個(gè)網(wǎng)絡(luò)編程模式,可以充分利用CSocket的特性。該模式的基礎(chǔ)是CSocketFile類。使用方法如下:
首先,構(gòu)造一個(gè)CSocket對(duì)象;調(diào)用Create函數(shù)創(chuàng)立一個(gè)socket對(duì)象(SOCK_STREAM類型)。
接著,假使是客戶程序,調(diào)用Connect連接到遠(yuǎn)地主機(jī);假使是服務(wù)器程序,先調(diào)用Listen監(jiān)聽socket端口,收到連接請(qǐng)求后調(diào)用Accept接收請(qǐng)求。
然后,創(chuàng)立一個(gè)和CSocket對(duì)象關(guān)聯(lián)的CSocketFile對(duì)象,創(chuàng)立一個(gè)和CSocketFile對(duì)象關(guān)聯(lián)的CArchive對(duì)象,指定CArchive對(duì)象是用于讀或者寫。假使既要讀又要寫,則創(chuàng)立兩個(gè)CArchive對(duì)象。
創(chuàng)立工作完成之后,使用CArchive對(duì)象在客戶和服務(wù)器之間傳送數(shù)據(jù)
使用完畢,銷毀CArchive對(duì)象、CSocketFile對(duì)象、CSocket對(duì)象。
從前面的章節(jié)可以知道,CArchive可以以一個(gè)CFile對(duì)象為基礎(chǔ),通過(guò)>操作符完成對(duì)文件的二進(jìn)制流的操作。所以可以從CFile派生一個(gè)類,實(shí)現(xiàn)CFile的操作界面(Read和Write)。由于CSocket提供了阻塞操作,所以完全可以像讀寫文件一樣讀寫socket數(shù)據(jù)。
當(dāng)Client端socket與Server端socket相互通信時(shí),兩端均會(huì)觸發(fā)socket事件。這里僅簡(jiǎn)要說(shuō)明兩個(gè)socket事件:
FD_CONNECT:連接事件,尋常Client端socket調(diào)用socketAPI函數(shù)Connect時(shí)所觸發(fā),這個(gè)事件發(fā)生在Client端。?FD_ACCEPT:正在引入的連接事件,尋常Server端socket正在接收來(lái)自Client端socket連接時(shí)觸發(fā),這個(gè)事件發(fā)生在Server端。
?
網(wǎng)絡(luò)傳輸服務(wù)進(jìn)程將socket事件保存至socket的事件隊(duì)列中。此外,網(wǎng)絡(luò)傳輸服務(wù)進(jìn)程還會(huì)向socketwindow發(fā)送消息WM_SOCKET_NOTIFY,通知有socket事件產(chǎn)生,見下文對(duì)socketwindow的詳細(xì)說(shuō)明。調(diào)用CSocket::Create函數(shù)后,socket被創(chuàng)立。socket創(chuàng)立過(guò)程中調(diào)用CAsyncSocket::AttachHandle(SOCKEThSocket,CAsyncSocket*pSocket,BOOLbDead)。該函數(shù)的作用是:
將socket實(shí)例句柄和socket指針添加至當(dāng)前模塊狀態(tài)(注1)的一個(gè)映射表變量m_pmapSocketHandle中。
?在AttachHandle過(guò)程中,會(huì)new一個(gè)CSocketWnd實(shí)例(基于CWnd派生),這里將這個(gè)實(shí)例稱之為socketwindow,進(jìn)一步理解為它是存放所有sockets的消息池(window消息),請(qǐng)細(xì)心查看,這里socket后多加了一個(gè)s,表示創(chuàng)立的多個(gè)socket將共享一個(gè)消息池。?當(dāng)Client端socket與Server端相互通信時(shí),此時(shí)網(wǎng)絡(luò)傳輸服務(wù)進(jìn)程向socketwindow發(fā)送消息WM_SOCKET_NOTIFY,需要說(shuō)明的是CSocketWnd窗口句柄保存在當(dāng)前模塊狀態(tài)的m_hSocketWindow變量中。
?
2、阻塞模式
阻塞模式下Server端與Client端之間的通信處于同步狀態(tài)下。在Server端直接實(shí)例化CSocket類,調(diào)用Create方法創(chuàng)立socket,然后調(diào)用方法Listen開始偵聽,最終用一個(gè)while循環(huán)阻塞調(diào)用Accept函數(shù)用于等待來(lái)自Client端的連接,假使這個(gè)socket在主線程(主程序)中運(yùn)行,這將導(dǎo)致主線程的阻塞。因此,需要?jiǎng)?chuàng)立一個(gè)新的線程以運(yùn)行socket服務(wù)。調(diào)試跟蹤至CSocket::Accept函數(shù)源碼:
while(!Accept(...)){
//Thesocketismarkedasnonblockingandnoconnectionsarepresenttobeaccepted.
if(GetLastError()==WSAEWOULDBLOCK)PumpMessage(FD_ACCEPT);else
returnFALSE;}
它不斷調(diào)用CAsyncSocket::Accept(CSocket派生自CAsyncSocket類)判斷Server端socket的事件隊(duì)列中是否存在正在引入的連接事件-
FD_ACCEPT(見1),換句話說(shuō),就是判斷是否有來(lái)自Client端socket的連接請(qǐng)求。
假使當(dāng)前Server端socket的事件隊(duì)列中存在正在引入的連接事件,Accept返回一個(gè)非0值。否則,Accept返回0,此時(shí)調(diào)用GetLastError將返回錯(cuò)誤代碼WSAEWOULDBLOCK,表示隊(duì)列中無(wú)任何連接請(qǐng)求。注意到在循環(huán)體內(nèi)有一句代碼:
PumpMessage(FD_ACCEPT);
PumpMessage作為一個(gè)消息泵使得socketwindow中的消息能夠維持在活動(dòng)狀態(tài)。實(shí)際跟蹤進(jìn)入PumpMessage中,發(fā)現(xiàn)這個(gè)消息泵與Accept函數(shù)的調(diào)用并不相關(guān),它只是使很少的socketwindow消息(典型的是WM_PAINT窗口重繪消息)處于活動(dòng)狀態(tài),而絕大部分的socketwindow消息被阻塞,被阻塞的消息中含有WM_SOCKET_NOTIFY。
很顯然,假使沒有來(lái)自Client端socket的連接請(qǐng)求,CSocket就會(huì)不斷調(diào)用Accept產(chǎn)生循環(huán)阻塞,直到有來(lái)自Client端socket的連接請(qǐng)求而解除阻塞。
阻塞解除后,表示Server端socket和Client端socket已成功連接,Server端與Client端彼此相互調(diào)用Send和Receive方法開始通信。
3、非阻塞模式
在非阻塞模式下利用socket事件的消息機(jī)制,Server端與Client端之間的通信處于異步狀態(tài)下。
尋常需要從CSocket類派生一個(gè)新類,派生新類的目的是重載socket事件的消息函數(shù),然后在socket事件的消息函數(shù)中添入適合的代碼以完成Client端與Server端之間的通信,與阻塞模式相比,非阻塞模式無(wú)需創(chuàng)立一
個(gè)新線程。
這里將探討當(dāng)Server端socket事件-FD_ACCEPT被觸發(fā)后,該事件的處理函數(shù)OnAccept是如何進(jìn)一步被觸發(fā)的。其它事件的處理函數(shù)如OnConnect,OnReceive等的觸發(fā)方式與此類似。
在1中已提到Client/Server端通信時(shí),Server端socket正在接收來(lái)自Client端socket連接請(qǐng)求,這將會(huì)觸發(fā)FD_ACCEPT事件,同時(shí)Server端的網(wǎng)絡(luò)傳輸服務(wù)進(jìn)程向Server端的socketwindow(CSocketWnd)發(fā)送事件通知消息WM_SOCKET_NOTIFY,通知有FD_ACCEPT事件產(chǎn)生,CsocketWnd在收到事件通知消息后,調(diào)用消息處理函數(shù)OnSocketNotify:
LRESULTCSocketWnd::OnSocketNotify(WPARAMwParam,LPARAMlParam){
CSocket::AuxQueueAdd(WM_SOCKET_NOTIFY,wParam,lParam);CSocket::ProcessAuxQueue();return0L;}
消息參數(shù)wParam是socket的句柄,lParam是socket事件。這里稍作解釋一下,CSocketWnd類是作為CSocket類的友元類,這意味著它可以訪問(wèn)CSocket類中的保護(hù)和私有成員函數(shù)和變量,AuxQueueAdd和
ProcessAuxQueue是CSocket類的靜態(tài)成員函數(shù),假使你對(duì)友元不熟悉,請(qǐng)迅速找本有關(guān)C++書看一下友元的使用方法吧!
ProcessAuxQueue是實(shí)質(zhì)處理socket事件的函數(shù),在該函數(shù)中有這樣一句代碼:
CAsyncSocket*pSocket=CAsyncSocket::LookupHandle((SOCKET)wParam,TRUE);
其實(shí)也就是由socket句柄得到發(fā)送事件通知消息的socket指針pSocket:從m_pmapSocketHandle中查找(見1)!
最終,WSAGETSELECTEVENT(lParam)會(huì)取出事件類型,在一個(gè)簡(jiǎn)單的switch語(yǔ)句中判斷事件類型并調(diào)用事件處理函數(shù)。在這里,事件類型是FD_ACCEPT,當(dāng)然就調(diào)用pSocket->OnAccept!
終止語(yǔ)
Server端socket處于阻塞調(diào)用模式下,它必需在一個(gè)新創(chuàng)立的線程中工作,防止主線程被阻塞。
當(dāng)有多個(gè)Client端socket與Server端socket連接及通信時(shí),
Server端采用阻塞模式就顯得不適合
溫馨提示
- 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ù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025-2030年中國(guó)條形碼標(biāo)簽打印機(jī)行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030年中國(guó)抗生素行業(yè)發(fā)展分析及前景趨勢(shì)與投資戰(zhàn)略研究報(bào)告
- 2025-2030年中國(guó)平行回線行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030年中國(guó)定型連褲襪和和緊身褲行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030年中國(guó)天然乳膠行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030年中國(guó)冷凝管行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030年中國(guó)體育用品行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030年中國(guó)乳膠行業(yè)市場(chǎng)現(xiàn)狀供需分析及投資評(píng)估規(guī)劃分析研究報(bào)告
- 2025-2030全球及中國(guó)床墊、百葉窗和窗簾行業(yè)市場(chǎng)現(xiàn)狀供需分析及市場(chǎng)深度研究發(fā)展前景及規(guī)劃可行性分析研究報(bào)告
- 農(nóng)作物抗逆性狀的精準(zhǔn)檢測(cè)與分析-洞察闡釋
- 羅氏C8000使用操作說(shuō)明
- 融資融券策略課件
- 單層鋼結(jié)構(gòu)廠房施工組織設(shè)計(jì)方案
- 項(xiàng)目盡職調(diào)查清單模板
- 唯物主義和經(jīng)驗(yàn)批判主義研讀課件
- 環(huán)境保護(hù)和水土保持保證體系框圖
- 【審計(jì)工作底稿模板】FH應(yīng)付利息
- 眼部健康檢測(cè)與分析課件
- 專業(yè)碩士學(xué)位論文修改報(bào)告(二)
- 蘇州市建設(shè)工程造價(jià)計(jì)價(jià)解釋
- 煤礦機(jī)電設(shè)備春季預(yù)防性檢修計(jì)劃
評(píng)論
0/150
提交評(píng)論