web-dev-qa-db-ja.com

ソケットでカスタムデータ構造を送信する適切な方法

ソケットによって構造体をchar配列として送信する適切な方法は何ですか?.

現時点で私は以下のような考えを持っていますが、未定義の動作を引き起こすため、それは良い解決策ではありません。文字列やその他のユーザー定義型などの型を送信することはできますか?

または、プロトコルフレームとして機能する構造体には、int、char、または固定サイズのchar配列などのbult-inタイプのみが含まれている必要がありますか?

たぶん、これについてのベストプラクティスを知っていますか?多分あなたはそれを行うための作業ライブラリを知っていますか?

私とは違う具体的な例は見つかりませんでした。

class FrameProtocol
{
public:
    std::array<char, 4096> buffer;
};

class FrameHeader
{
public:
    typedef enum { hello, priv} types;
    types type;
    const size_t size = sizeof(FrameHeader);
};

class PrivateMessage
{
public:
    int sender;
    int receiver;
    std::string value;
    const size_t size = sizeof(PrivateMessage);
};

class HelloMessage
{
public:
    std::string nickname;
    size_t size = sizeof(HelloMessage);
};


void sendTo(FrameProtocol& protocol)
{
    FrameHeader frame_header;
    memcpy(&frame_header, protocol.buffer.begin(), frame_header.size);

    std::cout << "Type: " << frame_header.type << std::endl;

    switch (frame_header.type)
    {
        case frame_header.priv:
        {
            std::cout << "###############################################" << std::endl;
            std::cout << "PRIV RECEIVED!" << std::endl;
            PrivateMessage priv;
            memcpy(&priv, protocol.buffer.begin() + frame_header.size, priv.size);
            std::cout << "PrivateMessage!, From: " << priv.sender << " To: " << priv.receiver << " val: " << priv.value << std::endl;
            std::cout << "################################################\n" << std::endl;
            break;
        }
        case frame_header.hello:
        {
            std::cout << "###############################################" << std::endl;
            std::cout << "HELLO RECEIVED!" << std::endl;
            HelloMessage hello {};
            memcpy(&hello, protocol.buffer.begin() + frame_header.size, sizeof(HelloMessage));
            std::cout << "HelloMessage!, Nickname: " << hello.nickname << std::endl;
            std::cout << "################################################\n" << std::endl;
        break;

            break;
        }

    }
}

int main()
{
    FrameProtocol frame;
    FrameHeader frame_header;
    frame_header.type = frame_header.priv;

    PrivateMessage pm;
    pm.sender = 10;
    pm.receiver = 20;
    pm.value = "Test";

    memcpy(frame.buffer.begin(), &frame_header, frame_header.size); // write header
    memcpy(frame.buffer.begin() + frame_header.size, &pm, pm.size); // write body

    FrameProtocol frame2;
    FrameHeader frame_header2;
    frame_header2.type = frame_header2.hello;

    HelloMessage hello;
    hello.nickname = "testas";
    hello.size = sizeof(hello);
    memcpy(frame2.buffer.begin(), &frame_header2, frame_header2.size); //write header
    memcpy(frame2.buffer.begin() + frame_header2.size, &hello, hello.size); // write body

    sendTo(frame2);
    sendTo(frame);
3
bielu000

ソケットを介してデータを送信する場合は、最初にデータを何らかのテキスト形式(JSONやXMLなど)に変換するのが最善です。

バイナリデータをインターネット経由で送信する場合の重要な問題は、通信チャネルの両側が同様のコンピューターアーキテクチャ(データのエンディアンや整数のサイズなど)で実行されることを想定していることです。 ASN.1、BSON、XDR、Thriftなどのバイナリデータ交換形式を使用して、これらの問題にも対処できます。

(たくさんあります)などの作業に使用できるライブラリには、gSOAP( https://www.cs.fsu.edu/~engelen/soap.html )、またはStroika( https://github.com/SophistSolutions/Stroika )。 GSoapを使用すると、C++構造を定義し、WSDLを生成し、これらのデータ構造で要求を送受信するための単純なクライアント/サーバープロキシ/スタブを作成できます。 Stroikaフレームワークには、通信を処理するための同様の組み込みWebサーバーとWebクライアントレイヤー、C++オブジェクトをJSON(またはXML、またはその他のテキスト形式)との間でマッピングするための使いやすいツール、およびWebサービスレイヤー機能の関連付けが含まれていますこのすべてを一緒に。

3
Lewis Pringle

このようなプロセスの標準で定義されていない動作については心配しないでください。 C++の低レベルの動作の多くは未定義です。重要なのは、あなたのプラットフォームに対して一貫した動作を得ることができるということです。したがって、プロトコルを決定し、送受信するバイトオーダーを正確に理解するカスタムデコード/エンコーディングを記述します。従来、これは「ネットワークバイトオーダー」(=ビッグエンディアン)でデータを送信し、リトルエンディアンアーキテクチャのバイトオーダーを変換することを意味していました。浮動小数点数は、あらゆる表現方法を使用できるため、理論的にはもっと厄介ですが、幸い、実際には、現在のアーキテクチャはIEEE 754を中心に統合されているため、それほど問題にはなりません。

また、送信するデータが簡単にコピーできるPODであることを確認する必要もあります。現在のクラスはstd::stringを含んでいるため、そうではありません。これらの文字列を送信可能な形式に再コード化する必要があります。

代わりに、データを人間が読めるテキストのみの形式(JSONやXMLなど)に変換しますが、これはバイナリアプローチよりも必然的に遅くなります。

0
Jack Aidley

最も簡単なのは、JSONとの変換です。これは、パフォーマンスが重要ではない場合に行うことであり、より複雑な構造で非常にうまく機能し、巧妙に使用すれば、フォーマットのアップグレードをサポートします。

次に、いくつかのバイナリ形式に変換するためのサードパーティのライブラリがあります。これは少し複雑ですが高速です。通常、これはビルドプロセスに統合する必要があります。

または、私がお勧めしない手動の方法:バイトの配列を使用し、バイトごとに入力します。

0
gnasher729