Home | Overview | How Do I | Sample
This article and two companion articles explain several issues in Windows Sockets programming. This article covers byte ordering. The other issues are covered in the articles: Windows Sockets: Blocking and Windows Sockets: Converting Strings.
If you use or derive from class CAsyncSocket, you will need to manage these issues yourself. If you use or derive from class CSocket, MFC manages them for you.
Different machine architectures sometimes store data using different byte orders. For example, Intel-based machines store data in the reverse order of Macintosh (Motorola) machines. Intel’s byte order, called “little-Endian,” is also the reverse of the network standard “big-Endian” order. The following table explains these terms.
Big- and Little-Endian Byte Ordering
Byte ordering | Meaning |
Big-Endian | The most significant byte is on the left end of a word. |
Little-Endian | The most significant byte is on the right end of a word. |
Typically, you don’t have to worry about byte-order conversion for data that you send and receive over the network, but there are situations in which you must convert byte orders.
You need to convert byte orders in the following situations:
You can avoid the work of converting byte orders in the following situations:
This is fairly easy to do, and the result is usually smaller, faster code. For information, see the MFC Migration Kit, which is included with Visual C++.
Working with CAsyncSocket, you must manage any necessary byte-order conversions yourself. Windows Sockets standardizes the “big-Endian” byte-order model and provides functions to convert between this order and others. CArchive, however, which you use with CSocket, uses the opposite (“little-Endian”) order — but CArchive takes care of the details of byte-order conversions for you. By using this standard ordering in your applications, or using Windows Sockets byte-order conversion functions, you can make your code more portable.
The ideal case for using MFC sockets is when you’re writing both ends of the communication: using MFC at both ends. If you’re writing an application that will communicate with non-MFC applications, such as an FTP server, you’ll probably need to manage byte-swapping yourself before you pass data to the archive object, using the Windows Sockets conversion routines, ntohs, ntohl, htons, and htonl. An example of these functions used in communicating with a non-MFC application appears later in this article.
Note When the other end of the communication is not an MFC application, you also must avoid streaming C++ objects derived from CObject into your archive because the receiver will not be able to handle them. See the Caution in the article Windows Sockets: Using Sockets with Archives.
For more information about byte orders, see the Windows Sockets specification, available in the Win32 SDK.
The following example shows a serialization function for a CSocket object that uses an archive. It also illustrates using the byte-order conversion functions in the Windows Sockets API.
This example presents a scenario in which you are writing a client that communicates with a non-MFC server application for which you have no access to the source code. In this scenario, you must assume that the non-MFC server uses standard network byte order. In contrast, your MFC client application uses a CArchive object with a CSocket object, and CArchive uses “little-Endian” byte order, the opposite of the network standard.
Suppose the non-MFC server with which you plan to communicate has an established protocol for a message packet like the following:
struct Message
{
long MagicNumber;
unsigned short Command;
short Param1;
long Param2;
};
In MFC terms, this would be expressed as follows:
struct Message
{
long m_lMagicNumber;
short m_nCommand;
short m_nParam1;
long m_lParam2;
void Serialize( CArchive& ar );
};
In C++, a struct is essentially the same thing as a class. The Message
structure can have member functions, such as the Serialize
member function declared above. The Serialize
member function might look like this:
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);
}
}
This example calls for byte-order conversions of data because there is a clear mismatch between the byte ordering of the non-MFC server application on one end and the CArchive used in your MFC client application on the other end. The example illustrates several of the byte-order conversion functions that Windows Sockets supplies. The following table describes these functions.
Windows Sockets Byte-Order Conversion Functions
Function | Purpose |
ntohs | Convert a 16-bit quantity from network byte order to host byte order (Big-Endian to Little-Endian). |
ntohl | Convert a 32-bit quantity from network byte order to host byte order (Big-Endian to Little-Endian). |
htons | Convert a 16-bit quantity from host byte order to network byte order (Little-Endian to Big-Endian). |
htonl | Convert a 32-bit quantity from host byte order to network byte order (Little-Endian to Big-Endian). |
Another point of this example is that when the socket application on the other end of the communication is a non-MFC application, you must avoid doing something like the following:
ar << pMsg;
where pMsg
is a pointer to a C++ object derived from class CObject. This will send extra MFC information associated with objects and the server won’t understand it, as it would if it were an MFC application.