⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mfc教程_ socket类的设计和实现.htm

📁 MFC (Microsoft Foundation Class Library)中的各种类结合起来构成了一个应用程序框架
💻 HTM
📖 第 1 页 / 共 3 页
字号:
    <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对象为基础,通过&lt;&lt;和&gt;&gt;操作符完成对文件的二进制流的操作。所以可以从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-&gt;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-&gt;GetSockOpt(SO_TYPE,&amp;nType,&amp;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 &gt; 0)</P>
<P align=justify>{</P>
<DIR>
<P align=justify>//CSocket的Receive,阻塞操作,读取到数据才继续</P>
<P align=justify>nRead = m_pSocket-&gt;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-&gt;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-&gt;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-&gt;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-&gt;Send(lpBuf, nCount);</P>
<P align=justify>if (nWritten == SOCKET_ERROR)</P>
<P align=justify>{</P>
<DIR>
<P align=justify>int nError = m_pSocket-&gt;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>&nbsp;</P>
<P align=justify> </P></BODY></HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -