C#ネットワークストリームでバイトを強制的に「フラッシュ」しないのはなぜですか?

c# networkstream sockets tcp tcpclient
C#ネットワークストリームでバイトを強制的に「フラッシュ」しないのはなぜですか?

シリアル化されたオブジェクトをサーバーに送信し、「OK」または「エラー」メッセージが返されるのを待つプロジェクトがあります。

私は次のポスターと同様の問題を抱えているようです:https://stackoverflow.com/questions/4898356/tcpclient-send-close-problem[TcpClient send / close problem]

問題は、元のオブジェクトを送信できると思われる唯一の方法は接続を閉じることですが、(もちろん)オブジェクトがサーバーによって正常に処理されたかどうかを確認するのが待ちきれないことです。

private void button4_Click(object sender, EventArgs e)
{
    RequestPacket req = new RequestPacket();

    /// ... Fill out request packet ...

    /// Connect to the SERVER to send the message...
    TcpClient Client = new TcpClient("localhost", 10287);
    using (NetworkStream ns = Client.GetStream())
    {
        XmlSerializer xml = new XmlSerializer(typeof(RequestPacket));
        xml.Serialize(ns, req);

        /// NOTE: This doesn't seem to do anything....
        ///       The server doesn't get the object I just serialized.
        ///       However, if I use ns.Close() it does...
        ///          but then I can't get the response.
        ns.Flush();

        // Get the response. It should be "OK".
        ResponsePacket resp;

        XmlSerializer xml2 = new XmlSerializer(typeof(ResponsePacket));
        resp = (ResponsePacket)xml2.Deserialize(ns);


        /// ... EVALUATE RESPONSE ...
    }

    Client.Close()
}

更新:1人のコメンターへの応答で、クライアントに問題があるとは思わない。 単にオブジェクトを待っているだけで、ソケットを閉じるまでオブジェクトは決して来ません。 しかし、私が間違っていれば、喜んでカラスを公に食べます。 =)クライアントは次のとおりです。

    static void Main(string[] args)
    {
        // Read the port from the command line, use 10287 for default
        CMD cmd = new CMD(args);
        int port = 10287;

        if (cmd.ContainsKey("p")) port = Convert.ToInt32(cmd["p"]);

        TcpListener l = new TcpListener(port);
        l.Start();

        while (true)
        {
            // Wait for a socket connection.
            TcpClient c = l.AcceptTcpClient();

            Thread T = new Thread(ProcessSocket);

            T.Start(c);
        }
    }


    static void ProcessSocket(object c)
    {
        TcpClient C = (TcpClient)c;

        try
        {
            RequestPacket rp;
            //// Handle the request here.
            using (NetworkStream ns = C.GetStream())
            {
                XmlSerializer xml = new XmlSerializer(typeof(RequestPacket));
                rp = (RequestPacket)xml.Deserialize(ns);
            }

            ProcessPacket(rp);
        }
        catch
        {
            // not much to do except ignore it and go on.
        }
    }

ええ…… とても簡単です。

  9  1


ベストアンサー

ああ、あなたはhttp://en.wikipedia.org/wiki/Nagle%27s_algorithm[Nagle’s algorithm]を責めることができます。 C#とは関係ありませんが、TCP / IPスタックのデフォルトの動作です。 http://msdn.microsoft.com/en-us/library/e160993dを使用して、http://msdn.microsoft.com/en-us/library/system.net.sockets.socketoptionname.aspx [NoDelay]ソケットオプションを有効にします。 aspx [SetSocketOption]メソッド。 ただし、Nagleのアルゴリズムを無効にするとスループットが低下することに注意してください。

私はまったくC#開発者ではないので、ソケットの上で使用しているストリームについてもわかりませんが、そのインスタンスをドロップして、確実に書き込むようにしてください:-)

3


短いバージョンは、XmlSerializer(またはその他の大きなblob)を使用してデータをNetworkStreamに押し込むと、単に行を開いたままにして、追加の情報が書き込まれるのを待つだけのようです。 接続を閉じると、接続がフラッシュされます。 これにより、このメソッドは送信には適していますが、受信には適していません。 またはその逆。 これは一方向の通信になり、同じ接続を介した継続的な往復通信には役に立ちません。

表面的にはエレガントに見えるものを回避する必要がありましたが、私は昔のC時代に戻って、最初に「バイト数」のパケット、次に実際のパケットを送信することに頼りました。 これにより、もう一方の端で正確なバイト数を読み取ることができるため、ブロックパターンに巻き込まれることはありません。

私の人生を単純化するために、送信と受信の両方の静的メソッドを保持するクラスを作成しました。 このクラスは、ネットワークを介して任意のXMLシリアル化可能なクラスを送信できるため、必要な処理を実行します。

誰もがよりエレガントなソリューションを持っている場合、私はそれを聞いてくれます。

public class PacketTransit
{
    public static void SendPacket(TcpClient C, object Packet)
    {
        MemoryStream ms = new MemoryStream();
        XmlSerializer xml = new XmlSerializer(Packet.GetType());
        xml.Serialize(ms, Packet);
        ms.Position = 0;
        byte[] b = ms.GetBuffer();
        ms.Dispose();

        byte [] sizePacket = BitConverter.GetBytes(b.Length);
        // Send the 4-byte size packet first.
        C.Client.Send(sizePacket, sizePacket.Length, SocketFlags.None);
        C.Client.Send(b, b.Length, SocketFlags.None);
    }

    /// The string is the XML file that needs to be converted.
    public static string ReceivePacket(TcpClient C, Type PacketType)
    {
        byte [] FirstTen = new byte[1024];
        int size = 0;
        byte[] sizePacket = BitConverter.GetBytes(size);

        // Get the size packet
        int sp = C.Client.Receive(sizePacket, sizePacket.Length, SocketFlags.None);
        if (sp <= 0) return "";

        size = BitConverter.ToInt32(sizePacket, 0);

        // read until "size" is met
        StringBuilder sb = new StringBuilder();
        while (size > 0)
        {
            byte[] b = new byte[1024];
            int x = size;
            if (x > 1024) x = 1024;
            int r = C.Client.Receive(b, x, SocketFlags.None);
            size -= r;
            sb.Append(UTF8Encoding.UTF8.GetString(b));
        }

        return sb.ToString();
    }

    /// The XML data that needs to be converted back to the appropriate type.
    public static object Decode(string PacketData, Type PacketType)
    {
        MemoryStream ms = new MemoryStream(UTF8Encoding.UTF8.GetBytes(PacketData));
        XmlSerializer xml = new XmlSerializer(PacketType);
        object obj = xml.Deserialize(ms);
        ms.Dispose();

        return obj;
    }

    public static RequestPacket GetRequestPacket(TcpClient C)
    {
        string str = ReceivePacket(C, typeof(RequestPacket));

        if (str == "") return new RequestPacket();

        RequestPacket req = (RequestPacket) Decode(str, typeof(RequestPacket));

        return req;
    }

    public static ResponsePacket GetResponsePacket(TcpClient C)
    {
        string str = ReceivePacket(C, typeof(ResponsePacket));

        if (str == "") return new ResponsePacket();

        ResponsePacket res = (ResponsePacket)Decode(str, typeof(ResponsePacket));

        return res;
    }
}

このクラスを使用するには、 PacketTransit.SendPacket(myTcpClient、SomePacket)`を呼び出して、指定されたXMLシリアル化可能なオブジェクトを送信するだけです。 次に、 `PacketTransit.GetResponsePacket`または PacketTransit.GetRequestPacket`を使用して、もう一方の端でそれを受信します。

私にとっては、これは非常にうまく機能していますが、当初の予想よりもかなり多くのトレーニングでした。

3


ネットワークストリームにリンクされたStreamWriter / Readerを使用する必要があります。
.Flush does nothing on a NetworkStream, see here:

0


ここでの本当の問題は、XmlDeserializerがストリームからEOSを読み取るまで戻らない可能性があることだと思います。 これを強制するには、出力の送信ストリームをシャットダウンする必要がある場合があります。

0


タイトルとURLをコピーしました