📄 mfc教程_ socket类的设计和实现.htm
字号:
<P align=justify>//阻塞操作。但不能同时进行两个阻塞操作。</P>
<P align=justify>if (m_pbBlocking != NULL)</P>
<P align=justify>{</P>
<P align=justify>WSASetLastError(WSAEINPROGRESS);</P>
<P align=justify>return FALSE;</P>
<P align=justify>}</P>
<P align=justify>//完成数据读取</P>
<P align=justify>int nResult;</P>
<P align=justify>while ((nResult = CAsyncSocket::Receive(lpBuf, nBufLen,
nFlags)) </P>
<P align=justify>== SOCKET_ERROR)</P>
<P align=justify>{</P>
<P align=justify>if (GetLastError() == WSAEWOULDBLOCK)</P>
<P align=justify>{</P>
<P align=justify>//进入消息循环,等待网络事件FD_READ</P>
<P align=justify>if (!PumpMessages(FD_READ))</P>
<P align=justify>return SOCKET_ERROR;</P>
<P align=justify>}</P>
<P align=justify>else</P>
<P align=justify>return SOCKET_ERROR;</P>
<P align=justify>}</P>
<P align=justify>return nResult;</P>
<P align=justify>}</P>
<P align=justify>其中:</P>
<P
align=justify>参数1指定一个缓冲区保存读取的数据;参数2指定缓冲区的大小;参数3取值MSG_PEEK(数据拷贝到缓冲区,但不从输入队列移走),或者MSG_OOB(处理带外数据),或者MSG_PEEK|MSG_OOB。</P>
<P
align=justify>Receive函数首先判断当前CSocket对象是否正在处理一个阻塞操作,如果是,则返回错误WSAEINPROGRESS;否则,开始数据读取的处理。</P>
<P
align=justify>读取数据时,如果基类CAsyncSocket的Receive读取到了数据,则返回;否则,如果返回一个错误,而且错误号是WSAEWOULDBLOCK,则表示操作阻塞,于是调用PumpMessage进入消息循环等待数据到达(网络事件FD_READ发生)。数据到达之后退出消息循环,再次调用CAsyncSocket的Receive读取数据,直到没有数据可读为止。</P>
<P align=justify>PumpMessages是CSocket的成员函数,它完成以下工作:</P>
<P align=justify>(1)设置m_pbBlocking,表示进入阻塞操作。</P>
<P
align=justify>(2)进行消息循环,如果有以下事件发生则退出消息循环:收到指定定时器的定时事件消息WM_TIMER,退出循环,返回TRUE;收到发送给本socket的消息WM_SOCKET_NOTIFY,网络事件FD_CLOSE或者等待的网络事件发生,退出循环,返回TRUE;发送错误或者收到WM_QUIT消息,退出循环,返回FALSE;</P>
<P
align=justify>(3)在消息循环中,把WM_SOCKET_DEAD消息和发送给其他socket的通知消息WM_SOCKET_NOFITY放进模块线程状态的通知消息列表m_listSocketNotifications,在阻塞操作完成之后处理;对其他消息,则把它们送给目的窗口的窗口过程处理。</P>
<P align=justify></P>
<LI><A name=_Toc452641023></A><A name=_Toc457299163></A><B>CSocketFile</B>
<P></P></LI></OL></OL>
<P
align=justify>MFC还提供了一个网络编程模式,可以充分利用CSocket的特性。该模式的基础是CSocketFile类。使用方法如下:</P>
<P align=justify>首先,构造一个CSocket对象;调用Create函数创建一个socket对象(SOCK_STREAM类型)。</P>
<P
align=justify>接着,如果是客户程序,调用Connect连接到远地主机;如果是服务器程序,先调用Listen监听socket端口,收到连接请求后调用Accept接收请求。</P>
<P
align=justify>然后,创建一个和CSocket对象关联的CSocketFile对象,创建一个和CSocketFile对象关联的CArchive对象,指定CArchive对象是用于读或者写。如果既要读又要写,则创建两个CArchive对象。</P>
<P align=justify>创建工作完成之后,使用CArchive对象在客户和服务器之间传送数据</P>
<P align=justify>使用完毕,销毁CArchive对象、CSocketFile对象、CSocket对象。</P>
<P
align=justify>从前面的章节可以知道,CArchive可以以一个CFile对象为基础,通过<<和>>操作符完成对文件的二进制流的操作。所以可以从CFile派生一个类,实现CFile的操作界面(Read和Write)。由于CSocket提供了阻塞操作,所以完全可以像读写文件一样读写socket数据。</P>
<P align=justify>下面,分析CSocketFile的设计和实现。</P>
<OL>
<P align=justify>
<LI>CSocketFile的构造函数和析构函数的实现
<P></P></LI></OL>
<UL>
<P align=justify>
<LI>构造函数的实现
<P></P></LI></UL>
<P align=justify>CSocketFile::CSocketFile(CSocket* pSocket, BOOL
bArchiveCompatible)</P>
<P align=justify>{</P>
<P align=justify>m_pSocket = pSocket;</P>
<P align=justify>m_bArchiveCompatible = bArchiveCompatible;</P>
<P align=justify></P>
<P align=justify>#ifdef _DEBUG</P>
<P align=justify>ASSERT(m_pSocket != NULL);</P>
<P align=justify>ASSERT(m_pSocket->m_hSocket != INVALID_SOCKET);</P>
<P align=justify></P>
<P align=justify>int nType = 0;</P>
<P align=justify>int nTypeLen = sizeof(int);</P>
<P
align=justify>ASSERT(m_pSocket->GetSockOpt(SO_TYPE,&nType,&nTypeLen));</P>
<P align=justify>ASSERT(nType == SOCK_STREAM);</P>
<P align=justify>#endif // _DEBUG</P>
<P align=justify>}</P>
<P align=justify>其中:</P>
<P align=justify>构造函数的参数1指向关联的CSocket对象,被保存在成员变量m_pSocket中;</P>
<P
align=justify>参数2指定该对象是否和一个CArchive对象关联(不关联则独立使用),被保存在成员变量bArchiveCompatible中。</P>
<P align=justify>Degug部分用于检测m_pSocket是否是SOCK_STREAM类型。</P>
<UL>
<P align=justify>
<LI>析构函数的实现
<P></P></LI></UL>
<P align=justify>CSocketFile::~CSocketFile()</P>
<P align=justify>{</P>
<P align=justify>}</P>
<P align=justify>(2)CSocketFile的读写的实现</P>
<P align=justify>分析CSocketFile如何用文件的读写实现网络I/O。</P>
<UL>
<P align=justify>
<LI>文件读的实现
<P></P></LI></UL>
<P align=justify>UINT CSocketFile::Read(void* lpBuf, UINT nCount)</P>
<P align=justify>{</P>
<P align=justify>ASSERT(m_pSocket != NULL);</P>
<P align=justify></P>
<P align=justify>int nRead;</P>
<P align=justify></P>
<P align=justify>//CSocketFile对象独立使用</P>
<P align=justify>if (!m_bArchiveCompatible)</P>
<P align=justify>{</P>
<DIR>
<P align=justify>int nLeft = nCount;</P>
<P align=justify>PBYTE pBuf = (PBYTE)lpBuf;</P>
<P align=justify></P>
<P align=justify>//读完nCount个字节的数据</P>
<P align=justify>while(nLeft > 0)</P>
<P align=justify>{</P>
<DIR>
<P align=justify>//CSocket的Receive,阻塞操作,读取到数据才继续</P>
<P align=justify>nRead = m_pSocket->Receive(pBuf, nLeft);</P>
<P align=justify>if (nRead == SOCKET_ERROR)</P>
<P align=justify>{</P>
<DIR>
<DIR>
<P align=justify>int nError = m_pSocket->GetLastError();</P>
<P align=justify>AfxThrowFileException(CFileException::generic, nError);</P>
<P align=justify>ASSERT(FALSE);</P></DIR></DIR>
<P align=justify>}</P>
<P align=justify>else if (nRead == 0)</P>
<P align=justify>{</P>
<DIR>
<DIR>
<P align=justify>return nCount - nLeft;</P></DIR></DIR>
<P align=justify>}</P>
<P align=justify></P>
<P align=justify>nLeft -= nRead;</P>
<P align=justify>pBuf += nRead;</P></DIR>
<P align=justify>}</P>
<P align=justify>return nCount - nLeft;</P></DIR>
<P align=justify>}</P>
<P align=justify></P>
<P align=justify>//和一个CArchive对象关联使用</P>
<P align=justify>//读取数据,能读多少是多少</P>
<P align=justify>nRead = m_pSocket->Receive(lpBuf, nCount, 0);</P>
<P align=justify>if (nRead == SOCKET_ERROR)</P>
<P align=justify>{</P>
<DIR>
<P align=justify>int nError = m_pSocket->GetLastError();</P>
<P align=justify>AfxThrowFileException(CFileException::generic, nError);</P>
<P align=justify>ASSERT(FALSE);</P></DIR>
<P align=justify>}</P>
<P align=justify>return nRead;</P>
<P align=justify>}</P>
<UL>
<P align=justify>
<LI>文件写的实现
<P></P></LI></UL>
<P align=justify>void CSocketFile::Write(const void* lpBuf, UINT nCount)</P>
<P align=justify>{</P>
<P align=justify>ASSERT (m_pSocket!=NULL);</P>
<P align=justify></P>
<P align=justify>//CSocket的函数Send,阻塞操作,发送完毕才继续</P>
<P align=justify>int nWritten = m_pSocket->Send(lpBuf, nCount);</P>
<P align=justify>if (nWritten == SOCKET_ERROR)</P>
<P align=justify>{</P>
<DIR>
<P align=justify>int nError = m_pSocket->GetLastError();</P>
<P align=justify>AfxThrowFileException(CFileException::generic,
nError);</P></DIR>
<P align=justify>}</P>
<P align=justify>}</P>
<P
align=justify>从CSockefFile的读写实现可以看出,CSocketFile如果独立使用,在Read操作时可能出现无限等待,因为数据是分多个消息多次送达的,没有读取到指定长度的数据并不表示数据读取完毕。但是和CArchive配合使用,则仅仅读取到数据就返回。至于数据是否读取完毕,可以使用CArchive的IsBufferEmpty函数来判断。</P>
<P align=justify>其他CFile界面,CSocketFile没有实现。</P>
<P
align=justify>从CScocketFile的设计和实现来看,CSocketFile是使用CSocket的一个很好的例子,也是使用CFile的一个例子。</P>
<HR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" align=center border=0>
<TBODY>
<TR>
<TD align=middle><A href="http://www.vczx.com/tutorial/mfc/mfc13.php"
target=_self>上一章</A> <A href="http://www.vczx.com/tutorial/mfc/mfc.php"
target=_self>回目录</A></TD></TR></TBODY></TABLE>
<P> </P>
<P align=justify> </P></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -