この記事と 2 つのコンパニオン記事では、Windows ソケット プログラミングのいくつかの問題について説明します。 この記事では、バイト順序について説明します。 その他の問題については、 Windows ソケット: ブロック と Windows ソケット: 文字列の変換に関する記事を参照してください。
クラス CAsyncSocket を使用または派生する場合は、これらの問題を自分で管理する必要があります。 クラス CSocket を使用または派生する場合は、MFC によって管理されます。
バイトの順序付け
異なるマシン アーキテクチャでは、異なるバイトオーダーを使用してデータが格納される場合があります。 たとえば、Intel ベースのマシンでは、Macintosh (Motorola) マシンの逆の順序でデータが格納されます。 "little-Endian" と呼ばれる Intel のバイト順は、ネットワーク標準の "big-Endian" 順序の逆でもあります。 次の表では、これらの用語について説明します。
大きいバイト順と Little-Endian バイト順
バイト順 | 意味 |
---|---|
Big-Endian | 最も重要なバイトは、単語の左端にあります。 |
Little-Endian | 最も重要なバイトは、単語の右端にあります。 |
通常、ネットワーク経由で送受信するデータのバイトオーダー変換について心配する必要はありませんが、バイトオーダーを変換する必要がある場合があります。
バイトオーダーを変換する必要がある場合
次の状況では、バイトオーダーを変換する必要があります。
別のコンピューターに送信するデータとは対照的に、ネットワークで解釈する必要がある情報を渡しています。 たとえば、ネットワークが理解する必要があるポートとアドレスを渡すことができます。
通信相手のサーバー アプリケーションは MFC アプリケーションではありません (また、ソース コードがありません)。 これは、2 つのマシンが同じバイト順序を共有していない場合にバイトオーダー変換を呼び出します。
バイトオーダーを変換する必要がない場合
次のような状況では、バイトオーダーの変換作業を回避できます。
両端のマシンはバイトをスワップしないことに同意でき、両方のマシンは同じバイト順を使用します。
通信しているサーバーは MFC アプリケーションです。
通信しているサーバーのソース コードがあるため、バイトオーダーを変換する必要があるかどうかを明示的に判断できます。
サーバーを MFC に移植できます。 これは非常に簡単に実行でき、結果は通常、より小さく、より高速なコードになります。
CAsyncSocket を操作するには、必要なバイトオーダー変換を自分で管理する必要があります。 Windows ソケットは、"big-Endian" バイトオーダー モデルを標準化し、この順序と他の順序の間で変換する関数を提供します。 ただし、CSocket で使用する CArchive は逆の ("little-Endian") 順序を使用しますが、CArchive
はバイトオーダー変換の詳細を処理します。 アプリケーションでこの標準の順序付けを使用するか、Windows ソケットのバイトオーダー変換関数を使用することで、コードの移植性を高めることができます。
MFC ソケットを使用する場合の理想的なケースは、通信の両端を記述する場合です。両端で MFC を使用します。 FTP サーバーなどの非 MFC アプリケーションと通信するアプリケーションを作成する場合は、Windows ソケット変換ルーチン ntohs、 ntohl、 htons、 htonl を使用して、アーカイブ オブジェクトにデータを渡す前に、バイトスワップを自分で管理する必要があります。 MFC 以外のアプリケーションとの通信に使用されるこれらの関数の例については、この記事の後半で説明します。
注
通信のもう一方の端が MFC アプリケーションでない場合は、受信側が処理できないため、 CObject
から派生した C++ オブジェクトをアーカイブにストリーミングしないようにする必要もあります。
「Windows ソケット: アーカイブでのソケットの使用」の注意事項を参照してください。
バイトオーダーの詳細については、Windows SDK で使用できる Windows ソケットの仕様を参照してください。
Byte-Order 変換の例
次の例は、アーカイブを使用する CSocket
オブジェクトのシリアル化関数を示しています。 また、Windows ソケット API でのバイト順変換関数の使用についても説明します。
この例では、ソース コードにアクセスできない MFC 以外のサーバー アプリケーションと通信するクライアントを作成するシナリオを示します。 このシナリオでは、MFC 以外のサーバーで標準のネットワーク バイト順が使用されていると想定する必要があります。 これに対し、MFC クライアント アプリケーションでは、CSocket
オブジェクトを持つCArchive
オブジェクトが使用され、CArchive
はネットワーク標準とは逆の "リトル エンディアン" バイト順を使用します。
通信する予定の 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 サーバー アプリケーションのバイト順序と、もう一方の側の MFC クライアント アプリケーションで使用される CArchive
の間に明確な不一致があるため、データのバイトオーダー変換を呼び出します。 この例では、Windows ソケットが提供するバイト順変換関数をいくつか示します。 次の表では、これらの関数について説明します。
Windows ソケット Byte-Order 変換関数
機能 | 目的 |
---|---|
ntohs | 16 ビットの数量をネットワークのバイトオーダーからホストバイトオーダー (big-Endian から little-Endian) に変換します。 |
ntohl | 32 ビットの数量をネットワークのバイトオーダーからホストのバイトオーダー (big-Endian から little-Endian) に変換します。 |
Htons | 16 ビットの数量をホストバイトオーダーからネットワークバイトオーダー (リトルエンディアンからビッグエンディアン) に変換します。 |
Htonl | 32 ビットの数量をホストのバイトオーダーからネットワークのバイト順 (リトル エンディアンからビッグ エンディアン) に変換します。 |
この例のもう 1 つのポイントは、通信のもう一方の端のソケット アプリケーションが MFC 以外のアプリケーションである場合は、次のような処理を避ける必要があるということです。
ar << pMsg;
ここで、 pMsg
はクラス CObject
から派生した C++ オブジェクトへのポインターです。 これにより、オブジェクトに関連付けられている追加の MFC 情報が送信され、MFC アプリケーションの場合と同様に、サーバーはそれを認識しません。
詳細については、以下を参照してください。