0

NetLinkソケットライブラリ(https://sourceforge.net/apps/wordpress/netlinksockets/)を使用していて、指定した形式でネットワーク経由でバイナリデータを送信したいと考えています。

私が計画したフォーマットは非常に単純で、次のとおりです。

  • バイト0および1:タイプuint16_tのオペコード(つまり、符号なし整数は常に2バイト長)

  • バイト2以降:文字列、整数、それぞれの組み合わせなど、必要なその他のデータ。相手方は、オペコードに従ってこのデータを解釈します。たとえば、「ログイン」を表すオペコードが0の場合、このデータは、ユーザー名の長さを示す1バイト整数、ユーザー名を含む文字列、パスワードを含む文字列で構成されます。オペコード1「チャットメッセージを送信する」の場合、ここのデータ全体はチャットメッセージの単なる文字列である可能性があります。

ただし、ライブラリがデータを送信するために使用できるものは次のとおりです。

void send(const string& data);
void send(const char* data);
void rawSend(const vector<unsigned char>* data);

これにはrawSend()を使用したいと思いますが、rawSend()は、メモリへのvoid *ポインタではなく、unsigned charsを取​​りますか?特定のタイプのデータをunsignedcharの配列にキャストしようとすると、ここでデータがいくらか失われることはありませんか?私が間違っている場合は訂正してください。しかし、私が正しい場合、これは、実際のバイナリデータ転送をサポートしている別のライブラリを調べる必要があることを意味しますか?

このライブラリが私の目的を果たしていると仮定すると、さまざまなデータ型を1つのstd :: vectorにキャストして連結するにはどうすればよいでしょうか?私が試したのは次のようなものです。

#define OPCODE_LOGINREQUEST 0

std::vector<unsigned char>* loginRequestData = new std::vector<unsigned char>();
uint16_t opcode = OPCODE_LOGINREQUEST;
loginRequestData->push_back(opcode);
// and at this point (not shown), I would push_back() the individual characters of the strings of the username and password.. after one byte worth of integer telling you how many characters long the username is (so you know when the username stops and the password begins)
socket->rawSend(loginRequestData);

ただし、データを解釈しようとしたときに、いくつかの例外が発生しました。キャスティングに近づいていますか?unsigned charsにキャストするとデータが失われますか?

前もって感謝します。

4

5 に答える 5

1

すべてと互換性があり、パフォーマンスに勝るものがないC標準タプルにフォールバックするのではなく、ベクトル(ヒープを使用する必要があるため、予測できない時間で実行する必要があります)を作成する方法が気に入っています。しかたがない。(const void* buffer, size_t len)

あなたはこれを試すことができます:

void send_message(uint16_t opcode, const void* rawData, size_t rawDataSize)
{
    vector<unsigned char> buffer;
    buffer.reserve(sizeof(uint16_t) + rawDataSize);
#if BIG_ENDIAN_OPCODE
    buffer.push_back(opcode >> 8);
    buffer.push_back(opcode & 0xFF);
#elseif LITTLE_ENDIAN_OPCODE
    buffer.push_back(opcode & 0xFF);
    buffer.push_back(opcode >> 8);
#else
    // Native order opcode
    buffer.insert(buffer.end(), reinterpret_cast<const unsigned char*>(&opcode), 
        reinterpret_cast<const unsigned char*>(&opcode) + sizeof(uint16_t));
#endif
    const unsigned char* base(reinterpret_cast<const unsigned char*>(rawData));
    buffer.insert(buffer.end(), base, base + rawDataSize);
    socket->rawSend(&buffer); // Why isn't this API using a reference?!
}

これは、を使用insertした手書きのループよりも最適化する必要があるを使用しpush_back()ます。rawSendまた、例外をスローしてもバッファをリークしません。

注:バイト順序は、この接続の両端のプラットフォームで一致する必要があります。そうでない場合は、1バイトの順序を選択してそれを維持するか(インターネット標準は通常これを行い、htonlandhtons関数を使用します)、バイト順序を検出する必要があります(「ネイティブ」または「逆方向」から受信者のPOV)、「後方」の場合は修正します。

于 2011-10-07T04:56:33.530 に答える
1

私はこのようなものを使用します:

#define OPCODE_LOGINREQUEST 0 
#define OPCODE_MESSAGE 1

void addRaw(std::vector<unsigned char> &v, const void *data, const size_t len)
{
    const unsigned char *ptr = static_cast<const unsigned char*>(data);
    v.insert(v.end(), ptr, ptr + len);
}

void addUint8(std::vector<unsigned char> &v, uint8_t val)
{
    v.push_back(val);
}

void addUint16(std::vector<unsigned char> &v, uint16_t val)
{
    val = htons(val);
    addRaw(v, &val, sizeof(uint16_t));
}

void addStringLen(std::vector<unsigned char> &v, const std::string &val)
{
    uint8_t len = std::min(val.length(), 255);
    addUint8(v, len);
    addRaw(v, val.c_str(), len);
}

void addStringRaw(std::vector<unsigned char> &v, const std::string &val)
{
    addRaw(v, val.c_str(), val.length());
}

void sendLogin(const std::string &user, const std::string &pass)
{
    std::vector<unsigned char> data(
        sizeof(uint16_t) +
        sizeof(uint8_t) + std::min(user.length(), 255) +
        sizeof(uint8_t) + std::min(pass.length(), 255)
    );
    addUint16(data, OPCODE_LOGINREQUEST);
    addStringLen(data, user);
    addStringLen(data, pass);
    socket->rawSend(&data);
}

void sendMsg(const std::string &msg)
{
    std::vector<unsigned char> data(
      sizeof(uint16_t) +
      msg.length()
    );
    addUint16(data, OPCODE_MESSAGE);
    addStringRaw(data, msg);
    socket->rawSend(&data);
}
于 2011-10-07T06:28:41.407 に答える
0
std::vector<unsigned char>* loginRequestData = new std::vector<unsigned char>();
uint16_t opcode = OPCODE_LOGINREQUEST;
loginRequestData->push_back(opcode);

が8ビット長の場合unsigned char(ほとんどのシステムではそうです)、opcodeプッシュするたびに上位8ビットが失われます。これについては警告が表示されるはずです。

rawSendを採用するという決定vectorは非常に奇妙です。一般的なライブラリは、異なるレベルの抽象化で機能します。rawSend渡されたデータのコピーを作成し、操作が完了するまでその存続期間を保証するため、この方法であると推測できます。そうでない場合は、設計上の選択としては不十分です。それに加えて、引数をポインタで取るという事実...これdataは生のメモリのコンテナと見なす必要があります。正しく理解するためのいくつかの癖がありますが、このシナリオでポッドタイプを操作する方法は次のとおりです。

data->insert( data->end(), reinterpret_cast< char const* >( &opcode ), reinterpret_cast< char const* >( &opcode ) + sizeof( opcode ) );
于 2011-10-07T04:31:36.120 に答える
0

これは機能します:

#define OPCODE_LOGINREQUEST 0

std::vector<unsigned char>* loginRequestData = new std::vector<unsigned char>();
uint16_t opcode = OPCODE_LOGINREQUEST;
unsigned char *opcode_data = (unsigned char *)&opcode;
for(int i = 0; i < sizeof(opcode); i++)
    loginRequestData->push_back(opcode_data[i]);
socket->rawSend(loginRequestData);

これは、すべてのPODタイプでも機能します。

于 2011-10-07T04:34:49.043 に答える
0

ええ、sendはおそらくNULLターミネータを期待しているので、rawSendを使用してください。

void*の代わりにcharにキャストしても何も失われません。記憶は記憶です。RTTI情報を除いて、型がC++のメモリに格納されることはありません。オペコードで指定されたタイプにキャストすることで、データを回復できます。

コンパイル時にすべての送信の形式を決定できる場合は、構造体を使用してそれらを表すことをお勧めします。私は以前にこれを専門的に行ったことがありますが、これはさまざまなメッセージの形式を明確に保存するための最良の方法です。そして、反対側で開梱するのはとても簡単です。オペコードに基づいて生のバッファを構造体にキャストするだけです!

struct MessageType1 {
    uint16_t opcode;
    int myData1;
    int myData2;
};

MessageType1 msg;

std::vector<char> vec;
char* end = (char*)&msg + sizeof(msg);
vec.insert( vec.end(), &msg, end );

send(vec);

構造体アプローチは、送受信するための最良の最も近い方法ですが、レイアウトはコンパイル時に固定されます。メッセージの形式が実行時まで決定されない場合は、char配列を使用してください。

char buffer[2048];

*((uint16_t*)buffer) = opcode;
// now memcpy into it
// or placement-new to construct objects in the buffer memory

int usedBufferSpace = 24; //or whatever

std::vector<char> vec;
const char* end = buffer + usedBufferSpace;
vec.insert( vec.end(), buffer, end );

send(&buffer);
于 2011-10-07T04:39:50.147 に答える