Windows 套接字:使用存档的套接字如何工作

本文说明如何组合 CSocket 对象、CSocketFile 对象和 CArchive 对象,以简化通过 Windows 套接字发送和接收数据。

Windows 套接字:使用存档的套接字的示例一文展示了 PacketSerialize 函数。 PacketSerialize 示例中的存档对象的工作方式与传递给 MFC Serialize 函数的存档对象非常类似。 本质区别在于,对于套接字,存档不会附加到标准 CFile(通常与磁盘文件关联),而是附加到 CSocketFile 对象。 CSocketFile 对象会连接到 CSocket 对象,而不是连接到磁盘文件。

CArchive 对象会管理缓冲区。 当存储(发送)存档的缓冲区已满时,关联 CFile 对象会写出缓冲区的内容。 刷新附加到套接字的存档缓冲区相当于发送消息。 当加载(接收)存档的缓冲区已满时,CFile 对象会停止读取,直到缓冲区再次可用。

CSocketFile 派生自 CFile,但它不支持 CFile 成员函数,例如定位函数(SeekGetLengthSetLength 等)、锁定函数(LockRangeUnlockRange)或 GetPosition 函数。 所有 CSocketFile 对象都必须对关联 CSocket 对象写入或读取字节序列。 由于不涉及文件,因此诸如 SeekGetPosition 此类的操作没有意义。 CSocketFile 派生自 CFile,因此通常继承所有这些成员函数。 若要防止这种情况,请在 CSocketFile 中替代不受支持的 CFile 成员函数以引发 CNotSupportedException

CSocketFile 对象会调用其 CSocket 对象的成员函数以发送或接收数据。

下图显示通信两端的这些对象间的关系。

CArchive, CSocketFile, and CSocket.
CArchive、CSocketFile 和 CSocket

这种明显复杂性的目的是使你无需自己管理套接字的细节。 你会创建套接字、文件和存档,然后通过将数据插入存档或从存档中提取数据来开始发送或接收数据。 CArchiveCSocketFileCSocket 会在后台管理细节。

CSocket 对象实际上是双状态对象:有时为异步(通常状态),有时为同步。 在异步状态下,套接字可以从框架接收异步通知。 但是在接收或发送数据等操作期间,套接字会变为同步状态。 这意味着,在同步操作完成之前,套接字不会接收进一步的异步通知。 因为它切换了模式,所以可以执行类似于以下的操作(举例而言):

void CMySocket::OnReceive(int nErrorCode)
{
   if (0 == nErrorCode)
   {
      CSocketFile file(this);
      CArchive ar(&file, CArchive::load);
      CString str;

      ar >> str;
   }
}

如果 CSocket 未作为双状态对象来实现,则在处理以前的通知时,可能会接收有关相同事件类型的其他通知。 例如,处理 OnReceive 时可能会收到 OnReceive 通知。 在上面的代码片段中,从存档中提取 str 可能会导致递归。 通过切换状态,CSocket 可阻止其他通知,从而防止递归。 一般规则是通知中没有通知。

注意

还可以将 CSocketFile 用作没有 CArchive 对象的(有限)文件。 默认情况下,CSocketFile 构造函数的 bArchiveCompatible 参数为 TRUE。 这指定文件对象用于存档。 若要在不使用存档的情况下使用文件对象,请将 FALSE 传入 bArchiveCompatible 参数

在“存档兼容”模式下,CSocketFile 对象提供更好的性能,并减少“死锁”的危险。当发送和接收套接字相互等待或等待常见资源时,会发生死锁。 如果 CArchive 对象像使用 CFile 对象那样使用 CSocketFile,出现这种情况。 借助 CFile,存档可以假定它接收的字节数少于请求的字节数,则已达文件末尾。 但是借助 CSocketFile,数据是基于消息的;缓冲区可以包含多个消息,因此接收的字节数少于请求的字节数不意味着到达文件末尾。 这种情况下应用程序不会受阻,因为借助 CFile,它可以继续从缓冲区读取消息,直到缓冲区为空。 这种情况下,CArchive 中的 IsBufferEmpty 函数可用于监视存档缓冲区的状态。

有关详细信息,请参阅 Windows 套接字:对存档使用套接字

另请参阅

MFC 中的 Windows 套接字
CObject::Serialize