设为首页 加入收藏

TOP

16.1.4 网络字节顺序
2013-10-07 13:10:00 来源: 作者: 【 】 浏览:59
Tags:16.1.4 网络 字节 顺序

16.1.4  网络字节顺序

不同机器架构使用不同的字节顺序存储数据,如基于Intel处理器的机器与Macintosh(Motorola)机器存储数据的字节顺序是相反的。Intel采用的字节顺序称为"小头方式",即低字节在前,高字节在后的方式,而标准的网络顺序是"大头方式",即高字节在前,低字节在后的方式。表示如下:

将0x12345678写入到以0x0000开始的内存中,则结果为

  1.                 大头方式        小头方式  
  2. 0x0000      0x12            0x34  
  3. 0x0001      0x34            0x12  
  4. 0x0002      0x56            0x78  
  5. 0x0003      0x78            0x56 

其中,0x12、0x34、0x56、0x78各是一个字节,组合字节顺序时,是以字Word为单位的,每个字包括两个字节,低字节和高字节。小头方式就是低字节在前,大头方式就是高字节在前。字之间是按照正常顺序。

一般情况下,用户不需要处理在网络上发送和接收数据的字节顺序的转换,但是在下列情况下,需要用户手动转换字节顺序。

用户传输的信息需要网络解释,这与发送到其他机器的数据不一样。如用户传输端口和地址时,必须由网络理解。

当与之通信的服务器应用程序不是MFC应用程序时,如果通信的两台机器使用的字节顺序不同,则需要调用字节转换。
而下列情况下,不需要用户手动调用字节转换。

两台机器使用相同的字节顺序,并且两端约定不进行字节交换。

与之通信的服务器是MFC应用程序。

用户有与之通信的服务器的源代码,因此,可以显式地说明是否转换字节顺序。

可以将服务器转换成MFC程序。

在后面会介绍到MFC的CAsyncSocket类,如果使用此类,用户必须自己管理需要的字节顺序转换。Windows Socket标准化"大头方式"字节顺序模型,并提供与"小头方式"字节顺序的转换函数。而CSocket使用的CArchive类使用"小头方式"字节顺序,但是CArchive类处理了字节顺序转换的细节。通过在应用程序中使用标准的字节顺序,或使用Windows Sockets字节顺序转换函数,用户可以编写灵活的代码。

如果使用MFC Sockets编程(www.cppentry.com),即客户端和服务器端都使用MFC,则不需要关心字节顺序的细节。如果编写与非MFC应用程序进行通信的应用程序,如FTP服务器,则用户在将数据传入存档对象前,需要自己管理字节顺序转换。Windows Sockets提供了4个转换函数,ntohs()、ntohl()、htons()和htonl(),如表16-1所示。

表16-1  字节转换函数

    < xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

   

ntohs()

16位数从网络字节顺序转换成主机

字节顺序,即从“大头方式”转换成“小头方式”

ntohl()

32位数从网络字节顺序转换成主机字

节顺序,即从“大头方式”转换成“小头方式”

htons()

16位数从主机字节顺序转换成网络字

节顺序,即从“小头方式”转换成“大头方式”

htonl()

32位数从主机字节顺序转换成网络字节顺

序,即从“小头方式”转换成“大头方式”


下面以使用存档的CSocket对象的序列化函数为例,说明如何使用字节转换顺序。假定与编写的程序通信的是非MFC服务器应用程序,并且没有源代码。此时,非MFC服务器使用的是标准网络字节顺序"大头方式"。编写的MFC客户端应用程序通过CSocket对象使用CArchive对象,而CArchive使用"小头方式"字节顺序。协议通信包为:

  1. struct MyPack                   // 自定义数据包  
  2. {  
  3.     long Number;                    // 流水号  
  4.     unsigned short Command;     // 命令标识  
  5.     short ParamA;               // 参数A  
  6.     short ParamB;               // 参数B  
  7. }; 

在MFC中,其定义为:

  1. struct MyPack                   // 自定义数据包  
  2. {  
  3.     long m_lNumber;                         // 流水号  
  4.     short m_nCommand;                       // 命令标识  
  5.     short m_nParamA;                            // 参数A  
  6.     short m_lParamB;                            // 参数B  
  7.     void Serialize( CArchive& ar );         // 序列化函数  
  8. }; 

其中,Serialize()是序列化函数,其定义为:

  1. void MyPack::Serialize(CArchive& ar)        // 自
    定义数据包类的序列化函数  
  2. {  
  3.     if (ar.IsStoring())                     
    // 如果是存储对象  
  4.     {  
  5.         ar << (DWORD)htonl(m_lMagicNumber); // 存储
    流水号为DWORD  
  6.         ar << (WORD)htons(m_nCommand);      // 存储
    命令标识为WORD  
  7.         ar << (WORD)htons(m_nParamA);       // 存储
    参数A为WORD  
  8.         ar << (WORD)htonl(m_lParamB);      
    // 存储参数B为WORD  
  9.     }  
  10.     else                                        
    // 如果是提取对象  
  11.     {  
  12.         WORD w;                             // 定义WORD变量  
  13.         DWORD dw;                           // 定义DWORD变量  
  14.         ar >> dw;                           // 提取DWORD值  
  15.         m_lMagicNumber = ntohl((long)dw);   // 将提
    取的DWORD值转换为流水号  
  16.         ar >> w ;                           // 提取WORD值  
  17.         m_nCommand = ntohs((short)w);       // 将提取
    的WORD值转换为命令标识  
  18.         ar >> w;                                // 提取WORD值  
  19.         m_nParamA = ntohs((short)w);            
    // 将提取的WORD值转换为参数A  
  20.         ar >> w;                               
    // 提取WORD值  
  21.         m_lParamB = ntohl((short)w);            
    // 将提取的WORD值转换为参数A  
  22.     }  

在本例中,非MFC服务器应用程序使用的字节顺序与客户端MFC应用程序CArchive字节顺序不同,因此,使用了字节顺序转换函数对数据进行了转换。当序列化函数存储数据时,首先将数据从主机顺序转换成网络顺序,然后将其存储;当序列化函数提取数据时,首先将数据从网络顺序转换成主机顺序,然后赋值给变量。

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇16.2.1 套接字函数 下一篇10.6 本章小结

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: