3

データを TCP サーバーにストリーミングする必要がありますが、これまでに行ったことはありません。データはバイナリ フレーム形式である必要があります。私はグーグルで検索し、このチュートリアルを見つけました。これは、私が何をする必要があるかを説明していると思います:

http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient(v=vs.71).aspx

しかし、必要な形式でデータを作成する方法がわからないので、正しい方法で送信できます。次の形式にする必要があります。

Field|Offset|    Type  | size(octets)
id   |  0   |unsign int|      1
name |  1   |Byte Array|      40
grade|  41  |sign float|      8 

例:

Field| Data | InBytes  |
id   |  133 | 133      |
name |  247 | 247 0    |
grade|  0   | 0        |

int、byte 配列、float を格納する必要があるデータ型と、オフセットとサイズを指定する場所、および最終的にサーバーに送信する方法 (この例では、バイト配列のみを送信する)。

AC# - 上記のリンクのコードを使用して、このようなデータを送信する方法の例をいただければ幸いです。

4

4 に答える 4

2

シリアル化(および逆シリアル化)されるデータを表すために、構造体を使用して適切なメタデータを設定できるため、CLRが残りの作業を行います。ここで他の人が言ったように、エンドポイントでのパケット受信に対処する必要があります。また、データに文字列フィールドがあるため、受信者が期待する文字セットを考慮する必要があります。次のコードは、構造体を実装して、管理対象データをコメント付きのバイナリ形式に変換する方法の例です。

// setting the layout to sequential will prevent the compiler/JIT
// to reorder the struct fields
// NOTE: Observe here that the Charset used is Ansi. You may need to
// change this depending on the format expected by the receiver.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct DataPacket
{

    [MarshalAs(UnmanagedType.U4)]
    public uint Id;

    // As I understood from your question, the Name field
    // has a prefixed size of 40 bytes. Attention here:
    // the SizeConst actually means '40 characters', not bytes
    // If you choose to use the Unicode charset, set SizeConst = 20
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
    public String Name;

    // This will be serialized in little endian format
    // in your question this field is 8 bytes long, which 
    // in c# corresponds to the double type. If you really mean
    // the float type (4 bytes), change here.
    public double Grade;

    // Calling this method will return a byte array with the contents
    // of the struct ready to be sent via the tcp socket.
    public byte[] Serialize()
    {
        // allocate a byte array for the struct data
        var buffer = new byte[Marshal.SizeOf(typeof(DataPacket))];

        // Allocate a GCHandle and get the array pointer
        var gch = GCHandle.Alloc(buffer, GCHandleType.Pinned);
        var pBuffer = gch.AddrOfPinnedObject();

        // copy data from struct to array and unpin the gc pointer
        Marshal.StructureToPtr(this, pBuffer, false);
        gch.Free();

        return buffer;
    }

    // this method will deserialize a byte array into the struct.
    public void Deserialize(ref byte[] data)
    {
        var gch = GCHandle.Alloc(data, GCHandleType.Pinned);
        this = (DataPacket)Marshal.PtrToStructure(gch.AddrOfPinnedObject(), typeof(DataPacket));
        gch.Free();
    }
}

使用法:

DataPacket packet;
packet.Id = 1234;
packet.Name = "Marvin the paranoid robot";
packet.Grade = 9.2;

// serialize
var bytes = packet.Serialize();

// send via tcp
var tcp = new TcpClient(...); 
tcp.GetStream().Write(bytes, 0, bytes.Length);


// deserializing;
DataPacket receivedPacket;
receivedPacket.Deserialize(bytes);

すでにパケットを持っているので、受信者でのパケット受信に対処する必要があります。その部分はすべて手作業で行う必要はありません。@jgauffinが言ったように、いくつかのツールを使用できます。

于 2012-08-15T15:03:29.747 に答える
2

私がとるアプローチは、コードの残りの部分と送信されるデータの物理表現との間のインターフェイスに 2 つの関数を用意することです。これにより、TCP 接続から送信される物理データ表現をカプセル化し、必要に応じて変更できるようになります。

したがって、おそらく必要なのは、フレーム構成機能とフレームの生バイトからデータ構造への変換を提供する TCP リーダー クラスと、データ構造を取得して生バイト フレームを作成する TCP ライター クラスを持つことです。それを TCP 接続に書き込みます。

バイトの配列になるメッセージバッファがあります。これは、TCP 接続から送信されるメッセージ バッファです。

次は、システムの残りの部分で使用されるデータを含むデータ構造です。

FromTcpToDataStruct (dataStruct, messageArray)次に、バイトの生の文字列から使用しているデータ構造に変換し、データ構造から生の文字列に変換する関数がありFromDataStructToTcp (messageArray, dataStruct)ます。

考慮しなければならない問題がいくつかあります。1 つ目は、ターゲット サーバーでの unsign int と float の表現である、リトル エンディアン ビッグ エンディアン問題です。2 つ目は、TCP 接続からのバイト文字列を個別のフレームに変換することです (TCP 接続からバイトを読み取り、中間バッファーを使用して未加工のバイトの完全なフレームを構成し、読み取りによってバイトの完全なフレームが提供されるようにします)。

于 2012-08-15T13:34:45.863 に答える
2

そのデータだけを送信することはできません。socket.Receive()TCP はストリーム ベースです。つまり、1 つの操作 ( ) または複数の操作を使用して情報を受信できます。

これを修正する最も簡単な方法は、ヘッダー (単純な長さのヘッダーで十分です) と本文 (指定した情報) を含むパケット形式にすることです。

すべてを自分で実装する必要があるのか​​、それともネットワーク ライブラリを使用できるのかはわかりません。すべてのソケット IO を処理するライブラリを作成しました。必要なのは、エンコードとデコードを処理することだけです。単純なバイナリ プロトコルの小さな例 (未完成) を作成しました。

コーダー/デコーダー クラスは、https ://github.com/jgauffin/griffin.networking/tree/master/Source/Protocols/SimpleBinary/Griffin.Networking.Protocol.SimpleBinary/Handlers にあります。

Griffin.Networking の紹介: http://blog.gauffin.org/2012/05/griffin-networking-a-somewhat-performant-networking-library-for-net/

于 2012-08-15T13:46:15.997 に答える
1

バイナリライターを使用してデータをバイト配列に変換し、例のように送信します。

文字列をチェックして、40バイトを形成していることを確認する必要があります。

        MemoryStream MS = new MemoryStream();
        BinaryWriter Writer = new BinaryWriter(MS);
        Writer.Write(MyByte);
        Writer.Write(ASCIIEncoding.UTF8.GetBytes(MyString));
        Writer.Write(MyDouble);
于 2012-08-15T13:35:31.333 に答える