本文和两篇配套文章介绍了 Windows 套接字编程中的几个问题。 本文介绍字节排序。 文章介绍了其他问题: Windows 套接字:阻止 和 Windows 套接字:转换字符串。
如果使用或派生自 类 CAsyncSocket,则需要自行管理这些问题。 如果使用或派生类 CSocket,MFC 会为你管理它们。
字节排序
不同的计算机体系结构有时会使用不同的字节订单存储数据。 例如,基于 Intel 的计算机以 Macintosh (Motorola) 计算机的反向顺序存储数据。 Intel 字节顺序,称为“little-Endian”,也是网络标准“big-Endian”顺序的反向。 下表说明了这些术语。
Big- and Little-Endian Byte Ordering
字节排序 | 含义 |
---|---|
Big-Endian | 最重要的字节位于单词的左端。 |
Little-Endian | 最重要的字节位于单词的右端。 |
通常,无需担心通过网络发送和接收的数据的字节顺序转换,但在某些情况下必须转换字节订单。
何时必须转换字节订单
需要在以下情况下转换字节订单:
你传递的信息需要由网络解释,而不是发送到另一台计算机的数据。 例如,可以传递网络必须理解的端口和地址。
要与之通信的服务器应用程序不是 MFC 应用程序(并且没有它的源代码)。 如果两台计算机不共享相同的字节排序,则此调用字节顺序转换。
当无需转换字节订单时
在以下情况下,可以避免转换字节订单的工作:
两端的计算机可以同意不交换字节,并且两台计算机使用相同的字节顺序。
你与之通信的服务器是一个 MFC 应用程序。
你有与你通信的服务器的源代码,因此可以显式判断你是否必须转换字节订单。
可以将服务器移植到 MFC。 这相当容易实现,结果通常是更小、更快的代码。
使用 CAsyncSocket 时,必须自行管理任何必要的字节顺序转换。 Windows 套接字标准化“big-Endian”字节顺序模型,并提供用于在此顺序和其他人之间进行转换的函数。 但是,与 CSocket 一起使用的 CArchive 使用相反(“little-Endian”)顺序,但CArchive
需要处理字节顺序转换的详细信息。 通过在应用程序中使用此标准排序,或使用 Windows 套接字字节顺序转换函数,可以使代码更易于移植。
使用 MFC 套接字的理想情况是编写通信的两端:在两端使用 MFC。 如果要编写将与非 MFC 应用程序(例如 FTP 服务器)通信的应用程序,则可能需要先自行管理字节交换,然后再使用 Windows 套接字转换例程 ntohs、ntohl、htons 和 htonl 来管理字节交换。 本文稍后将介绍用于与非 MFC 应用程序通信的这些函数示例。
注释
当通信的另一端不是 MFC 应用程序时,还必须避免流式传输C++派 CObject
生自存档的对象,因为接收方无法处理它们。 请参阅 Windows 套接字中的说明:将套接字与存档配合使用。
有关字节订单的详细信息,请参阅 Windows SDK 中提供的 Windows 套接字规范。
Byte-Order 转换示例
以下示例演示使用存档的对象的序列化函数 CSocket
。 它还演示了在 Windows 套接字 API 中使用字节顺序转换函数。
此示例演示了一种方案,在此方案中,你正在编写一个客户端,该客户端与你无权访问源代码的非 MFC 服务器应用程序通信。 在此方案中,必须假定非 MFC 服务器使用标准网络字节顺序。 相比之下,MFC 客户端应用程序使用 CArchive
对象与 CSocket
对象,并使用 CArchive
“little-Endian”字节顺序,这与网络标准相反。
假设计划与之通信的非 MFC 服务器具有消息数据包的已建立协议,如下所示:
struct Message
{
long MagicNumber;
unsigned short Command;
short Param1;
long Param2;
};
在 MFC 术语中,如下所示:
struct Message
{
long m_lMagicNumber;
short m_nCommand;
short m_nParam1;
long m_lParam2;
void Serialize(CArchive &ar);
};
在C++中,一个 struct
本质上与类相同。 该 Message
结构可以具有成员函数,例如 Serialize
上面声明的成员函数。 成员 Serialize
函数可能如下所示:
void Message::Serialize(CArchive &ar)
{
if (ar.IsStoring())
{
ar << (DWORD)htonl(m_lMagicNumber);
ar << (WORD)htons(m_nCommand);
ar << (WORD)htons(m_nParam1);
ar << (DWORD)htonl(m_lParam2);
}
else
{
WORD w;
DWORD dw;
ar >> dw;
m_lMagicNumber = ntohl((long)dw);
ar >> w;
m_nCommand = ntohs((short)w);
ar >> w;
m_nParam1 = ntohs((short)w);
ar >> dw;
m_lParam2 = ntohl((long)dw);
}
}
此示例调用数据的字节顺序转换,因为一端的非 MFC 服务器应用程序的字节顺序与 CArchive
另一端 MFC 客户端应用程序中使用的字节顺序存在明显的不匹配。 该示例演示了 Windows 套接字提供的几个字节顺序转换函数。 下表描述了这些函数。
Windows 套接字 Byte-Order 转换函数
功能 | 目的 |
---|---|
ntohs | 将 16 位数量从网络字节顺序转换为托管字节顺序(big-Endian 到 little-Endian)。 |
ntohl | 将 32 位数量从网络字节顺序转换为托管字节顺序(big-Endian 到 little-Endian)。 |
Htons | 将 16 位数量从主机字节顺序转换为网络字节顺序(little-Endian 到 big-Endian)。 |
Htonl | 将 32 位数量从主机字节顺序转换为网络字节顺序(little-Endian 到 big-Endian)。 |
此示例的另一点是,当通信的另一端的套接字应用程序是非 MFC 应用程序时,必须避免执行以下作:
ar << pMsg;
其中 pMsg
指向派生自类 CObject
的C++对象的指针。 这将发送与对象关联的额外 MFC 信息,服务器不会理解它,就像它是 MFC 应用程序一样。
有关详细信息,请参见: