




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
本文格式为Word版,下载可任意编辑——SocketCTCP阻塞非阻塞总结
gethostbyaddr、gethostbyname、gethostname、getprotolbyname
getprotolbynumber、getserverbyname、getservbyport。
第三类是Berkekleysocket例程的Windows专用的扩展函数,如gethostbyname对应的WSAAsynGetHostByName(其他数据库函数除了gethostname都有异步版本),select对应的WSAAsynSelect,判断是否阻塞的函数WSAIsBlocking,得到上一次WindsockAPI错误信息的WSAGetLastError,等等。
从另外一个角度,这些函数又可以分为两类,一是阻塞函数,一是非阻塞函数。所谓阻塞函数,是指其完成指定的任务之前不允许程序调用另一个函数,在Windows下还会阻塞本线程消息的发送。所谓非阻塞函数,是指操作启动之后,假使可以马上得到结果就返回结果,否则返回表示结果需要等待的错误信息,不等待任务完成函数就返回。
首先,异步函数是非阻塞函数;
其次,获取远地信息的数据库函数是阻塞函数(因此,WinSock提供了其异步版本);
在Berkeleysocket函数部分中,不涉及网络I/O、本地端工作的函数是非阻塞函数;
在Berkeleysocket函数部分中,网络I/O的函数是可阻塞函数,也就是它们可以阻塞执行,也可以不阻塞执行。这些函数都使用了一个socket,假使它们使用的socket是阻塞的,则这些函数是阻塞函数;假使它们使用的socket是非阻塞的,则这些函数是非阻塞函数。
创立一个socket时,可以指定它是否阻塞。在缺省状况下,Berkerley的Socket函数和WinSock都创立“阻塞〞的socket。阻塞socket通过使用select函数或者WSAAsynSelect函数在指定操作下变成非阻塞的。WSAAsyncSelect函数原型如下。
intWSAAsyncSelect(
SOCKETs,
HWNDhWnd,
u_intwMsg,
longlEvent);
其中,参数1指定了要操作的socket句柄;参数2指定了一个窗口句柄;参数3指定了一个消息,参数4指定了网络事件,可以是多个事件的组合,如:
FD_READ准备读
FD_WRITE准备写
FD_OOB带外数据到达
FD_ACCEPT收到连接
FD_CONNECT完成连接
FD_CLOSE关闭socket。
用OR操作组合这些事件值,如FD_READ|FD_WRITE
WSAAsyncSelect函数表示对sockets监测lEvent指定的网络事件,假使有事件发生,则给窗口hWnd发送消息wMsg。
假定应用程序的一个sockets指定了监测FD_READ事件,则在FD_READ事件上变成非阻塞的。当read函数被调用时,不管是否读到数据都马上返回,假使返回一个错误信息表示还在等待,则在等待的数据到达后,消息wMsg发送给窗口hWnd,应用程序处理该消息读取网络数据。
对于异步函数的调用,以类似的过程最终得到结果数据。以gethostbyname的异步版本的使用为例进行说明。该函数原型如下:
HANDLEWSAAsyncGetHostByName(
HWNDhWnd,
u_intwMsg,
constcharFAR*name,
charFAR*buf,
intbuflen);
在调用WSAAsyncGetHostByName启动操作时,不仅指定主机名字name,还指定了一个窗口句柄hWnd,一个消息IDwMsg,一个缓冲区及其长度。假使不能马上得到主机地址,则返回一个错误信息表示还在等待。当要的数据到达时,WinSockDLL给窗口hWnd发送消息wMsg告知得到了主机地址,窗口过程从指定的缓冲区buf得到主机地址。
使用异步函数或者非阻塞的socket,主要是为了不阻塞本线程的执行。在多进程或者多线程的状况下,可以使用两个线程通过同步手段来完成异步函数或者非阻塞函数的功能。
2.Socket的使用
WinSock以DLL的形式提供,在调用任何WinSockAPI之前,必需调用函数WSAStartup进行初始化,最终,调用函数WSACleanUp作清理工作。
MFC使用函数AfxSocketInit包装了函数WSAStartup,在WinSock应用程序的初始化函数IninInstance中调用AfxSocketInit进行初始化。程序不必调用WSACleanUp。
Socket是网络通信过程中端点的抽象表示。Socket在实现中以句柄的形式被创立,包含了进行网络通信必需的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
要使用socket,首先必需创立一个socket;然后,按要求配置socket;接着,按要求通过socket接收和发送数据;最终,程序关闭此socket。
为了创立socket,使用socket函数得到一个socket句柄:
socket_handle=socket(protocol_family.Socket_type,protocol);
其中:protocol_family指定socket使用的协议,取值PF_INET,表示Internet(TCP/IP)协议族;Socket_type指socket面向连接或者使用数据报;第三个参数表示使用TCP或者UDP协议。
当一个socket被创立时,WinSock将为一个内部结构分派内存,在此结构中保存此socket的信息,到此,socket连接使用的协议已经确定。
创立了socket之后,配置socket:
对于面向连接的客户,WinSock自动保存本地IP地址和选择协议端口,但是必需使用connect函数配置远地IP地址和远地协议端口:
result=connect(socket_handle,remote_socket_address,address_length)
remote_socket_address是一个指向特定socket结构的指针,该地址结构为socket保存了地址族、协议端口、网络主机地址。
面向连接的服务器则使用bind指定本地信息,使用listen和accept获取远地信息。
使用数据报的客户或者服务器使用bind给socket指定本地信息,在发送或者接收数据时指定远地信息。
bind给socket指定一个本地IP地址和协议端口,如下:
result=bind(socket_hndle,local_socket_address,address_length)
参数类型同connect。
函数listen监听bind指定的端口,假使有远地客户请求连接,使用accept接收请求,创立一个新的socket,并保存信息。
socket_new=accept(socket_listen,socket_address,address_length)
在socket配置好之后,使用socket发送或者接收数据:
面向连接的socket使用send发送数据,recv接收数据;
使用数据报的socket使用sendto发送数据,recvfrom接收数据。
1.MFC对WinSocktAPI的封装
MFC提供了两个类CAsyncSocket和CSocket来封装WinSockAPI,这给程序员提供了一个更简单的网络编程接口。
CAsyncSocket在较低层次上封装了WinSockAPI,缺省状况下,使用该类创立的socket是非阻塞的socket,所有操作都会马上返回,假使没有得到结果,返回WSAEWOULDBLOCK,表示是一个阻塞操作。
CSocket建立在CAsyncSocket的基础上,是CAsyncSocket的派生类。也就是缺省状况下使用该类创立的socket是非阻塞的socket,但是CSocket的网络I/O是阻塞的,它在完成任务之后才返回。CSocket的阻塞不是建立在“阻塞〞socket的基础上,而是在“非阻塞〞socket上实现的阻塞操作,在阻塞期间,CSocket实现了本线程的消息循环,因此,虽然是阻塞操作,但是并不影响消息循环,即用户依旧可以和程序交互。
1.CAsyncSocket
CAsyncSocket封装了低层的WinSockAPI,其成员变量m_hSocket保存其对应的socket句柄。使用CAsyncSocket的方法如下:
首先,在堆或者栈中构造一个CAsyncSocket对象,例如:
CAsyncSocketsock;或者
CAsyncSocket*pSock=newCAsyncSocket;
其次,调用Create创立socket,例如:
使用缺省参数创立一个面向连接的socket
sock.Create()
指定参数参数创立一个使用数据报的socket,本地端口为30
pSocket.Create(30,SOCK_DGRM);
其三,假使是客户程序,使用Connect连接到远地;假使是服务程序,使用Listen监听远地的连接请求。
其四,使用成员函数进行网络I/O。
最终,销毁CAsyncSocket,析构函数调用Close成员函数关闭socket。
下面,分析CAsyncSocket的几个函数,从中可以看到它是如何封装低层的WinSockAPI,简化有关操作的;还可以看到它是如何实现非阻塞的socket和非阻塞操作。
2.socket对象的创立和捆绑
(1)Create函数
首先,探讨Create函数,分析socket句柄如何被创立并和CAsyncSocket对象关联。Create的实现如下:
BOOLCAsyncSocket::Create(UINTnSocketPort,intnSocketType,
longlEvent,LPCTSTRlpszSocketAddress)
{
if(Socket(nSocketType,lEvent))
{
if(Bind(nSocketPort,lpszSocketAddress))
returnTRUE;
intnResult=GetLastError();
Close();
WSASetLastError(nResult);
}
returnFALSE;
}
其中:
参数1表示本socket的端口,缺省是0,假使要创立数据报的socket,则必需指定一个端口号。
参数2表示本socket的类型,缺省是SOCK_STREAM,表示面向连接类型。
参数3是屏蔽位,表示希望对本socket监测的事件,缺省是FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE。
参数4表示本socket的IP地址字符串,缺省是NULL。
Create调用Socket函数创立一个socket,并把它捆绑在this所指对象上,监测指定的网络事件。参数2和3被传递给Socket函数,假使希望创立数据报的socket,不要使用缺省参数,指定参数2是SOCK_DGRM。
假使上一步骤成功,则调用bind给新的socket分派端口和IP地址。
(2)Socket函数
接着,分析Socket函数,其实现如下:
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;
}
其中:
参数1表示Socket类型,缺省值是SOCK_STREAM。
参数2表示希望监测的网络事件,缺省值同Create,指定了全部事件。
参数3表示使用的协议,缺省是0。实际上,SOCK_STREAM类型的socket使用TCP协议,SOCK_DGRM的socket则使用UDP协议。
参数4表示地址族(地址格式),缺省值是PF_INET(等同于AF_INET)。对于TCP/IP来说,协议族和地址族是同值的。
在socket没有被创立之前,成员变量m_hSocket是一个无效的socket句柄。Socket函数把协议族、socket类型、使用的协议等信息传递给WinSockAPI函数socket,创立一个socket。假使创立成功,则把它捆绑在this所指对象。
(3)捆绑(Attatch)
捆绑过程类似于其他Windows对象,将在模块线程状态的WinSock映射中添加一对新的映射:this所指对象和新创立的socket对象的映射。
另外,假使本模块线程状态的“socket窗口〞没有创立,则创立一个,该窗口
在异步操作时用来接收WinSock的通知消息,窗口句柄保存到模块线程状态的m_hSocketWindow变量中。函数AsyncSelect将指定该窗口为网络事件消息的接收窗口。
函数AttachHandle的实现在此不列举了。
(4)指定要监测的网络事件
在捆绑完成之后,调用AsyncSelect指定新创立的socket将监测的网络事件。AsyncSelect实现如下:
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;
}
函数参数lEvent表示希望监视的网络事件。
_afxSockThreadState得到的是当前的模块线程状态,m_hSocketWindow是本模块在当前线程的“socket窗口〞,指定监视m_hSocket的网络事件,如指定事件发生,给窗口m_hSocketWindow发送WM_SOCKET_NOTIFY消息。
被指定的网络事件对应的网络I/O将是异步操作,是非阻塞操作。例如:指定FR_READ导致Receive是一个异步操作,假使不能马上读到数据,则返回一个错误WSAEWOULDBLOCK。在数据到达之后,WinSock通知窗口m_hSocketWindow,导致OnReceive被调用。
指定FR_WRITE导致Send是一个异步操作,即使数据没有送出也返回一个错误WSAEWOULDBLOCK。在数据可以发送之后,WinSock通知窗口m_hSocketWindow,导致OnSend被调用。
指定FR_CONNECT导致Connect是一个异步操作,还没有连接上就返回错误信息WSAEWOULDBLOCK,在连接完成之后,WinSock通知窗口m_hSocketWindow,导致OnConnect被调用。
对于其他网络事件,就不一一解释了。
所以,使用CAsyncSocket时,假使使用Create缺省创立socket,则所有网络I/O都是异步操作,进行有关网络I/O时则必需覆盖以下的相关函数:
OnAccept、OnClose、OnConnect、OnOutOfBandData、OnReceive、OnSend。
(5)Bind函数
经过上述过程,socket创立完毕,下面,调用Bind函数给m_hSocket指定本地端口和IP地址。Bind的实现如下:
BOOLCAsyncSocket::Bind(UINTnSocketPort,LPCTSTRlpszSocketAddress)
{
USES_CONVERSION;
//使用WinSock的地址结构构造地址信息
SOCKADDR_INsockAddr;
memset(
//得到地址参数的值
LPSTRlpszAscii=T2A((LPTSTR)lpszSocketAddress);
//指定是Internet地址类型
sockAddr.sin_family=AF_INET;
if(lpszAscii==NULL)
//没有指定地址,则自动得到一个本地IP地址
//把32比特的数据从主机字节序转换成网络字节序
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分派一个端口(1024—5000)
//把16比特的数据从主机字节序转换成网络字节序
sockAddr.sin_port=htons((u_short)nSocketPort);
//Bind调用WinSockAPI函数bind
returnBind((SOCKADDR*)
}
其中:函数参数1指定了端口;参数2指定了一个包含本地地址的字符串,缺省是NULL。
函数Bind首先使用结构SOCKADDR_IN构造地址信息。该结构的域sin_family表示地址格式(TCP/IP同协议族),赋值为AF_INET(Internet地址格式);域
sin_port表示端口,假使参数1为0,则WinSock分派一个端口给它,范围在1024和5000之间;域sin_addr是表示地址信息,它是一个联合体,其中s_addr表示如下形式的字符串,“28.56.22.8〞。假使参数没有指定地址,则WinSock自动地得到本地IP地址(假使有几个网卡,则使用其中一个的地址)。
(6)总结Create的过程
首先,调用socket函数创立一个socket;然后把创立的socket对象映射到CAsyncSocket对象(捆绑在一起),指定本socket要通知的网络事件,并创立一个“socket窗口〞来接收网络事件消息,最终,指定socket的本地信息。
下一步,是使用成员函数Connect连接远地主机,配置socket的远地信息。函数Connect类似于Bind,把指定的远地地址转换成SOCKADDR_IN对象表示的地址信息(包括网络字节序的转换),然后调用WinSock函数Connect连接远地主机,配置socket的远地端口和远地IP地址。
3.异步网络事件的处理
当网络事件发生时,“socket窗口〞接收WM_SOCKET_NOTIFY消息,消息处理函数OnSocketNotify被调用。“socket窗口〞的定义和消息处理是MFC实现的,这里不作详细的探讨。
OnSocketNotify回调CAsyncSocket的成员函数DoCallBack,DoCallBack调用事件处理函数,如OnRead、OnWrite等。摘录DoCallBack的一段代码如下:
switch(WSAGETSELECTEVENT(lParam))
{
caseFD_READ:
{
DWORDnBytes;
//得到可以一次读取的字节数
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的消息参数,OnSocketNotify传递给函数DoCallBack,表示通知事件。
函数IOCtl是CAsyncSocket的成员函数,用来对socket的I/O进行控制。这里的使用表示本次调用Receive函数至多可以读nBytes个字节。
从上面的探讨可以看出,从创立socket到网络I/O,CAsyncSocket直接封装了低层的WinSockAPI,简化了WinSock编程,实现了一个异步操作的界面。假使希望某个操作是阻塞操作,则在调用Create时不要指定该操作对应的网
络事件。例如,希望Connect和Send是阻塞操作,在任务完成之后才返回,则可以使用如下的语句:
pSocket->Create(0,SOCK_STREAM,
FR_WRITE|FR_OOB|FR_ACCEPT|FR_CLOSE);
这样,在Connect和Send时,假使是用户界面线程的话,可能阻塞线程消息循环。所以,最好在工线程中使用阻塞操作。
2.CSocket
假使希望在用户界面线程中使用阻塞socket,则可以使用CSocket。它在非阻塞socket基础之上实现了阻塞操作,在阻塞期间实现了消息循环。
对于CSocket,处理网络事件通知的函数OnAccept、OnClose、OnReceive依旧可以使用,OnConnect、OnSend在CSocket中永远不会被调用,另外OnOutOfBandData在CSocket中不勉励使用。
CSocket对象在调用Connect、Send、Accept、Close、Receive等成员函数后,这些函数在完成任务之后(连接被建立、数据被发送、连接请求被接收、socket被关闭、数据被读取)之后才会返回。因此,Connect和Send不会导致OnConnect和OnSend被调用。假使覆盖虚拟函数OnReceive、OnAccept、OnClose,不主动调用Receive、Accept、Close,则在网络事件到达之后导致对应的虚拟函数被调用,虚拟函数的实现应当调用Receive、Accept、Close来完成操作。下面,就一个函数Receive来考察CSocket如何实现阻塞操作和消息循环的。
intCSocket::Receive(void*lpBuf,intnBufLen,intnFlags)
{
//m_pbBlocking是CSocket的成员变量,用来标识当前是否正在进行
//阻塞操作。但不能同时进行两个阻塞操作。
if(m_pbBlocking!=NULL)
{
WSASetLastError(WSAEINPROGRESS);
returnFALSE;
}
//完成数据读取
intnResult;
while((nResult=CAsyncSocket::Receive(lpBuf,nBufLen,nFlags))
==SOCKET_ERROR)
{
if(GetLastError()==WSAEWOULDBLOCK)
{
//进入消息循环,等待网络事件FD_READ
if(!PumpMessages(FD_READ))
returnSOCKET_ERROR;
}
else
returnSOCKET_ERROR;
}
returnnResult;
}
其中:
参数1指定一个缓冲区保存读取的数据;参数2指定缓冲区的大小;参数3取值MSG_PEEK(数据拷贝到缓冲区,但不从输入队列移走),或者MSG_OOB(处理带外数据),或者MSG_PEEK|MSG_OOB。
Receive函数首先判断当前CSocket对象是否正在处理一个阻塞操作,假使是,则返回错误WSAEINPROGRESS;否则,开始数据读取的处理。
读取数据时,假使基类CAsyncSocket的Receive读取到了数据,则返回;否则,如
果返回一个错误,而且错误号是WSAEWOULDBLOCK,则表示操作阻塞,于是调用PumpMessage进入消息循环等待数据到达(网络事件FD_READ发生)。数据到达之后退出消息循环,再次调用CAsyncSocket的Receive读取数据,直到没有数据可读为止。
PumpMessages是CSocket的成员函数,它完成以下工作:
(1)设置m_pbBlocking,表示进入阻塞操作。
(2)进行消息循环,假使有以下事件发生则退出消息循环:收到指定定时器的定时事件消息WM_TIMER,退出循环,返回TRUE;收到发送给本socket的消息WM_SOCKET_NOTIFY,网络事件FD_CLOSE或者等待的网络事件发生,退出循环,返回TRUE;发送错误或者收到WM_QUIT消息,退出循环,返回FALSE;
(3)在消息循环中,把WM_SOCKET_DEAD消息和发送给其他socket的通知消息WM_SOCKET_NOFITY放进模块线程状态的通知消息列表m_listSocketNotifications,在阻塞操作完成之后处理;对其他消息,则把它们送给目的窗口的窗口过程处理。
3.CSocketFile
MFC还提供了一个网络编程模式,可以充分利用CSocket的特性。该模式的基础是CSocketFile类。使用方法如下:
首先,构造一个CSocket对象;调用Create函数创立一个socket对象(SOCK_STREAM类型)。
接着,假使是客户程序,调用Connect连接到远地主机;假使是服务器程序,先调用Listen监听socket端口,收到连接请求后调用Accept接收请求。
然后,创立一个和CSocket对象关联的CSocketFile对象,创立一个和CSocketFile对象关联的CArchive对象,指定CArchive对象是用于读或者写。假使既要读又要写,则创立两个CArchive对象。
创立工作完成之后,使用CArchive对象在客户和服务器之间传送数据
使用完毕,销毁CArchive对象、CSocketFile对象、CSocket对象。
从前面的章节可以知道,CArchive可以以一个CFile对象为基础,通过>操作符完成对文件的二进制流的操作。所以可以从CFile派生一个类,实现CFile的操作界面(Read和Write)。由于CSocket提供了阻塞操作,所以完全可以像读写文件一样读写socket数据。
当Client端socket与Server端socket相互通信时,两端均会触发socket事件。这里仅简要说明两个socket事件:
FD_CONNECT:连接事件,寻常Client端socket调用socketAPI函数Connect时所触发,这个事件发生在Client端。?FD_ACCEPT:正在引入的连接事件,寻常Server端socket正在接收来自Client端socket连接时触发,这个事件发生在Server端。
?
网络传输服务进程将socket事件保存至socket的事件队列中。此外,网络传输服务进程还会向socketwindow发送消息WM_SOCKET_NOTIFY,通知有socket事件产生,见下文对socketwindow的详细说明。调用CSocket::Create函数后,socket被创立。socket创立过程中调用CAsyncSocket::AttachHandle(SOCKEThSocket,CAsyncSocket*pSocket,BOOLbDead)。该函数的作用是:
将socket实例句柄和socket指针添加至当前模块状态(注1)的一个映射表变量m_pmapSocketHandle中。
?在AttachHandle过程中,会new一个CSocketWnd实例(基于CWnd派生),这里将这个实例称之为socketwindow,进一步理解为它是存放所有sockets的消息池(window消息),请细心查看,这里socket后多加了一个s,表示创立的多个socket将共享一个消息池。?当Client端socket与Server端相互通信时,此时网络传输服务进程向socketwindow发送消息WM_SOCKET_NOTIFY,需要说明的是CSocketWnd窗口句柄保存在当前模块状态的m_hSocketWindow变量中。
?
2、阻塞模式
阻塞模式下Server端与Client端之间的通信处于同步状态下。在Server端直接实例化CSocket类,调用Create方法创立socket,然后调用方法Listen开始侦听,最终用一个while循环阻塞调用Accept函数用于等待来自Client端的连接,假使这个socket在主线程(主程序)中运行,这将导致主线程的阻塞。因此,需要创立一个新的线程以运行socket服务。调试跟踪至CSocket::Accept函数源码:
while(!Accept(...)){
//Thesocketismarkedasnonblockingandnoconnectionsarepresenttobeaccepted.
if(GetLastError()==WSAEWOULDBLOCK)PumpMessage(FD_ACCEPT);else
returnFALSE;}
它不断调用CAsyncSocket::Accept(CSocket派生自CAsyncSocket类)判断Server端socket的事件队列中是否存在正在引入的连接事件-
FD_ACCEPT(见1),换句话说,就是判断是否有来自Client端socket的连接请求。
假使当前Server端socket的事件队列中存在正在引入的连接事件,Accept返回一个非0值。否则,Accept返回0,此时调用GetLastError将返回错误代码WSAEWOULDBLOCK,表示队列中无任何连接请求。注意到在循环体内有一句代码:
PumpMessage(FD_ACCEPT);
PumpMessage作为一个消息泵使得socketwindow中的消息能够维持在活动状态。实际跟踪进入PumpMessage中,发现这个消息泵与Accept函数的调用并不相关,它只是使很少的socketwindow消息(典型的是WM_PAINT窗口重绘消息)处于活动状态,而绝大部分的socketwindow消息被阻塞,被阻塞的消息中含有WM_SOCKET_NOTIFY。
很显然,假使没有来自Client端socket的连接请求,CSocket就会不断调用Accept产生循环阻塞,直到有来自Client端socket的连接请求而解除阻塞。
阻塞解除后,表示Server端socket和Client端socket已成功连接,Server端与Client端彼此相互调用Send和Receive方法开始通信。
3、非阻塞模式
在非阻塞模式下利用socket事件的消息机制,Server端与Client端之间的通信处于异步状态下。
寻常需要从CSocket类派生一个新类,派生新类的目的是重载socket事件的消息函数,然后在socket事件的消息函数中添入适合的代码以完成Client端与Server端之间的通信,与阻塞模式相比,非阻塞模式无需创立一
个新线程。
这里将探讨当Server端socket事件-FD_ACCEPT被触发后,该事件的处理函数OnAccept是如何进一步被触发的。其它事件的处理函数如OnConnect,OnReceive等的触发方式与此类似。
在1中已提到Client/Server端通信时,Server端socket正在接收来自Client端socket连接请求,这将会触发FD_ACCEPT事件,同时Server端的网络传输服务进程向Server端的socketwindow(CSocketWnd)发送事件通知消息WM_SOCKET_NOTIFY,通知有FD_ACCEPT事件产生,CsocketWnd在收到事件通知消息后,调用消息处理函数OnSocketNotify:
LRESULTCSocketWnd::OnSocketNotify(WPARAMwParam,LPARAMlParam){
CSocket::AuxQueueAdd(WM_SOCKET_NOTIFY,wParam,lParam);CSocket::ProcessAuxQueue();return0L;}
消息参数wParam是socket的句柄,lParam是socket事件。这里稍作解释一下,CSocketWnd类是作为CSocket类的友元类,这意味着它可以访问CSocket类中的保护和私有成员函数和变量,AuxQueueAdd和
ProcessAuxQueue是CSocket类的静态成员函数,假使你对友元不熟悉,请迅速找本有关C++书看一下友元的使用方法吧!
ProcessAuxQueue是实质处理socket事件的函数,在该函数中有这样一句代码:
CAsyncSocket*pSocket=CAsyncSocket::LookupHandle((SOCKET)wParam,TRUE);
其实也就是由socket句柄得到发送事件通知消息的socket指针pSocket:从m_pmapSocketHandle中查找(见1)!
最终,WSAGETSELECTEVENT(lParam)会取出事件类型,在一个简单的switch语句中判断事件类型并调用事件处理函数。在这里,事件类型是FD_ACCEPT,当然就调用pSocket->OnAccept!
终止语
Server端socket处于阻塞调用模式下,它必需在一个新创立的线程中工作,防止主线程被阻塞。
当有多个Client端socket与Server端socket连接及通信时,
Server端采用阻塞模式就显得不适合
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 江西幼师面试题及答案
- 岗位竞聘面试题及答案
- T/CAEPI 43-2022电絮凝法污水处理技术规程
- 中山路支行2025年上半年工作总结模版
- 中班教育教学工作方案模板
- T/CADBM 72-2023集装箱式方舱医院给水排水技术标准
- 占用绿化用地赔偿协议书
- 女儿继承父亲遗产协议书
- 中医医院对口帮扶协议书
- 卖车暂时过户合同范本
- 《宫颈妊娠业务学习》课件
- 《环糊精包合技术》课件
- 《讲卫生勤洗手》课件
- 膈肌麻痹学习课件
- 死亡登记培训课件
- 八年级数学思政融合课教学设计勾股定理
- 肝脏手术中的止血技术与挑战
- 信息安全专业大学生职业生涯规划书
- 建筑物沉降监测应急预案
- 路面弯沉温度修正系数
- 第三方工程验收报告范本
评论
0/150
提交评论