2011-02-10 4 views
0

私はソケットを介してUDPパケットのメインフレームからデータを受け取るCプログラムを持っています。 CプログラムのホストがUnix(ビッグエンディアン)からLinux(リトルエンディアン)に変更されており、プログラムはもはや機能しません。現在、メインフレームクライアントプログラムを変更するオプションはありません。リトルエンディアンシステムでビッグエンディアンシステムによって送信された構造体データの非直列化

プログラムはrecvfromを実行し、char配列にデータを受け取ります。以前は、このバッファをMFから渡されたものと一致する構造にキャストするだけで、すべてが完璧に機能しました。バイトアライメントが異なるため、構造へのマッピングが失敗します。ここに構造といくつかのコードがあります。

struct CCClntPkt 
{ 
    unsigned short packet_type; 
    unsigned short reply_socket; 
    unsigned long msg_ID; 
    unsigned short msg_length; 
    unsigned char client_string[250]; 
}; 

以前にこの構造体に、受信データバッファをキャストするために使用されるコードは次のようになります。

char BCpacket_in[MAX_PACKET]; 
struct CCClntPkt *pClntPkt; 

<snip> 

rcv_cnt = recvfrom(BCServerSocket, BCpacket_in, 
       sizeof(BCpacket_in),0x0,(struct sockaddr *)&from, 
       &fromlen); 

if (rcv_cnt > 0) 
{ 
    pClntPkt = (struct CCClntPkt *) &BCpacket_in; 
} 

私はntohsが、文字フィールドを使用してpacket_typeとreply_socketの正しい値を取得することができましたclient_stringはマングリングされています。私も構造の後にpragma pack(1)pragma pack(0)を入れてみましたが、依然として位置合わせの問題があるようです。

また、ビットシフト値をBCpacket_inから試して、packet_typeとreply_socketの正しい値を取得することができましたが、ulong msg_IDを引き出す方法がわかりません。そのコードは次のとおりです:

packet_type = BCpacket_in[0] << 8; 
packet_type |= BCpacket_in[1]; 

reply_to_socket = BCpacket_in[2] << 8; 
reply_to_socket |= BCpacket_in[3]; 

/* 
msg_ID = BCpacket_in[4] << 24; 
msg_ID |= BCpacket_in[5] << 16; 
msg_ID |= BCpacket_in[6] << 8; 
msg_ID |= BCpacket_in[7]; 
*/ 

私はこの時点で非常に困っていますので、何か助けに感謝します。私はこのプログラムの元の著者ではなく、私の知識はかなり限られています。私は仕事をしても構いませんので、関連する参考文献も提供されていることを感謝します。ありがとう!

+0

あなたがコメントアウトしたコードは、それが32ビットマシンであると仮定して、そのトリックを行うべきです。 char << intは整数昇格後の結果の型intを返します。 16ビットマシンでは、intは16ビットであり、コードは機能しません。 – Lundin

答えて

3

受信したパケット(BCpacket_in)を手動でstruct CCClntPktパケットに解析する必要がありますが、それはこれを実行する唯一の移植可能な方法です。エンディアンの変換は、ntohl(ネットワークからホストへのホストlong)ファミリーの関数で正しく処理されます。マンページbyteorder(3)endian(3)を参照してください。

これらの関数は、インターネット標準であるため、すべてのパケットがビッグエンディアンとしてワイヤを介して送信されることを前提としています。

#define PACKED __attribute__((__packed__)) 
struct PACKED message { ... }; 

これはGCC固有で、hereを参照してください。これは私が通常送らパック構造のために何をすべきかです

0

//ネットワークから受信します。次に、longのサイズを調べる必要があります。 32ビットおよび64ビットプラットフォームでは異なります。代わりにstdint.hタイプを使用して調べることができます。情報__builtin_bswap32() and __builtin_bswap64() GCC intrinsicsも見てください。

1
msg_ID = BCpacket_in[4] << 24; 
msg_ID |= BCpacket_in[5] << 16; 
msg_ID |= BCpacket_in[6] << 8; 
msg_ID |= BCpacket_in[7]; 

これは私にとって正しいようです。

署名付きの問題から身を守るために、あなたのバッファにunsigned charを使用してください。

BTWはビッグエンディアンのmsg_idで、オフセットは確実です:クライアント側では「パッキング」は機能しませんでしたので、パッキングルールを使用して構造体がワイヤに送信されると判断できますメインフレームの

+1

'ntohl'の何が問題なのですか?バイトスワップ操作の組み込みアセンブラ操作を使用するので、x86-linuxではビットシフト機構よりも速くすべきです。 – bdonlan

+0

ntohl()は変数を必要とし、ポインタでは機能しません。 ntohl()を使用する場合は、アドレスが正しく整列されていないか、バイトコピーを2倍しているntohl()を呼び出すよりも長い変数にバイトをコピーする必要がある場合、longへのポインタを作成する必要があります。 – ydroneaud

+0

@bdonlan 32ビット論理シフトは、効率的に勝ることはかなり難しいです。配列の索引付けとは別に、単一のCPU命令に変換する必要があります。私はあなたが2つの方法の間に速度の違いに気づかないと思う。 – Lundin

2

ビッグエンディアンのホストと新しいリトルエンディアンのホストとで、さまざまな種類のサイズが異なる可能性があります。

あなたのホストのあなたの両方にこのプログラムをコンパイルする場合、それはあなたのstructのサイズとレイアウトが表示されます。特に

#include <stddef.h> 
#include <stdio.h> 

struct CCClntPkt 
{ 
    unsigned short packet_type; 
    unsigned short reply_socket; 
    unsigned long msg_ID; 
    unsigned short msg_length; 
    unsigned char client_string[250]; 
}; 

int main() 
{ 
    printf("sizeof(unsigned short) = %u\n", (unsigned)sizeof(unsigned short)); 
    printf("sizeof(unsigned long) = %u\n", (unsigned)sizeof(unsigned long)); 

    printf("offsetof(struct CCClntPkt, reply_socket) = %u\n", (unsigned)offsetof(struct CCClntPkt, reply_socket)); 
    printf("offsetof(struct CCClntPkt, msg_ID) = %u\n", (unsigned)offsetof(struct CCClntPkt, msg_ID)); 
    printf("offsetof(struct CCClntPkt, msg_length) = %u\n", (unsigned)offsetof(struct CCClntPkt, msg_length)); 
    printf("offsetof(struct CCClntPkt, client_string) = %u\n", (unsigned)offsetof(struct CCClntPkt, client_string)); 

    return 0; 
} 

、それはlongがよりあなたの新しいホストになったことはかなり可能ですあなたの古いもの。これは、<stdint.h>のC99の正確な幅のタイプを使用するのに適しています。元のホストでshortが16ビットタイプで、longが32ビットタイプの場合は、それぞれuint16_tuint32_tに置き換えてください。

次に、エンディアン補正を実行するのに、ntohs()ntohl()を使用できます。

関連する問題