2

ソケット接続があり、このソケットを介してデータを送信しています。私が接続しているサーバーは、私のデータのすべての正しい送信に応答します。私はメッセージを機能させているので、受信したすべてのメッセージに対して回答を得ることができます。場合によっては、サーバーがメッセージを数秒間保持したり、別の順序で送信したりすることがあります。私の解決策は、スレッドを生成し、受信関数を中心にスピンさせることでした。ただし、MSDN のソケットの例を使用すると、うまくいきません。シンプルな do/while ループ構造を使用します。これを行うと、返信がごちゃごちゃになったり、データが不完全になったりします。これは宿題のためなので、より簡単なソリューションを使用するのではなく、クライアントを手動で作成する必要があります。このコードに何か問題がありますか?私はそれを長い間見つめてきたので、単純なものが欠けていると思います:

private static void ReceiveThread(Socket sock, ReceiverClass rc)
{
    // Create a socket and pass in parameter converted from object socket
    int receivedBytes = 0;

    do
    {
        // receive data from socket
        receivedBytes = sock.Receive(rc.buffer);
        byte[] formattedMsg = new byte[receivedBytes];
        Array.Copy(rc.buffer, formattedMsg, receivedBytes);
        rc.sb.Append("<LF><CR>" + System.Text.Encoding.ASCII.GetString(formattedMsg) + "\r\n");
    }
    while (receivedBytes > 0);
}

EDIT、受信スレッドを生成する機能を追加します。(長すぎますが、愚かなことを動かしたらきれいにするつもりです):

    public void SendData(Socket sock)
    {
        // Set socket timeout
        sock.ReceiveTimeout = 4000;

        // Prepare file for IO operations
        string path = @"c:\Logs\Lab2.Scenario3.WurdingerO.txt";
        StreamWriter logWrite = File.AppendText(path);

        // Get local ip address:
        IPAddress ip = Dns.GetHostAddresses(Dns.GetHostName()).Where(address => address.AddressFamily == AddressFamily.InterNetwork).First();
        string portNum = ((IPEndPoint)sock.LocalEndPoint).Port.ToString();

        // response time for scenario 2 and 3
        int responseTime = 0;

        // Set up Stopwatch to keep track of time
        Stopwatch stpWatch = new Stopwatch();
        stpWatch.Start();

        // setup for logging class
        ReceiverClass rc = new ReceiverClass();

        // setup receiving thread
        Thread receiveThread = new Thread(delegate()
            {
                ReceiveThread(sock, rc);
            });
        receiveThread.Start();

        // Counter to call client operations
        for (int i = 0; i < MESSAGE_COUNT; i++)
        {
            string msTime = Convert.ToString(stpWatch.ElapsedMilliseconds);
            if (msTime.Length > 10)
            {
                string newMSTime = "";

                for (int t = 9; t >= 0; t++)
                {
                    newMSTime += msTime[t];
                }
                msTime = newMSTime;
            }

            Classes.RequestBuilder reqB = new Classes.RequestBuilder();

            byte[] sendMsg;

            switch (scenarioNo)
            {
                case 1:
                    sendMsg = reqB.MessageBuildScenarioOne(sock, msTime,
                        ip.ToString(), portNum, serverPort, serverIP, i);
                    break;
                case 2:
                    // set up response time delay
                    switch (i)
                    {
                        case 1:
                            responseTime = 1000;
                            break;
                        case 3:
                            responseTime = 3000;
                            break;
                        default: 
                            responseTime = 0;
                            break;
                    }

                    sendMsg = reqB.MessageBuildScenarioTwo(sock, msTime,
                        ip.ToString(), portNum, serverPort, serverIP, i, responseTime);

                    break;
                case 3:
                    // set up response time delay
                    switch (i)
                    {
                        case 1:
                            responseTime = 1000;
                            break;
                        case 3:
                            responseTime = 3000;
                            break;
                        default:
                            responseTime = 0;
                            break;
                    }

                    sendMsg = reqB.MessageBuildScenarioThree(sock, msTime,
                        ip.ToString(), portNum, serverPort, serverIP, i, responseTime);

                    break;
                default:
                    sendMsg = reqB.MessageBuildScenarioOne(sock, msTime,
                        ip.ToString(), portNum, serverPort, serverIP, i);

                    break;
            }

            try
            {
                sock.Send(sendMsg);
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
            }
        }

        // Socket shutdown
        sock.Shutdown(SocketShutdown.Send);

        receiveThread.Join();

        sock.Shutdown(SocketShutdown.Receive);

        string date = System.DateTime.Now.ToString("MMddyyyy");
        string time = System.DateTime.Now.ToString("HHmmss");

        logWrite.Write(rc.sb.ToString());
        logWrite.Write(date + "|" + time + "|0|0|");

        // Close log file
        logWrite.Close();
        System.Windows.Forms.MessageBox.Show("Finished");
    }

編集:送信操作の後にスリープタイマーを入れたところ、問題が修正されました。ありがとうございました!

4

1 に答える 1

0

同様の問題があったと思います。

Socket クラスを使用して JSON 文字列を送信していたところ、突然 JSON.NET が例外をスローしました。文字列を調べたところ、2 つの JSON (ルート) オブジェクトが処理されていることがわかりました。次に、最小限の例をまとめたところ、ソケットが未読のデータを stash* することがわかりました (unread = no .Receive はまだ呼び出されておらず、.Receive は stash をクリアします)。

*stash: これが正しい用語かどうかはわかりません。それは私には理にかなっています。

これが私のテストです。実行する場合は、新しいテスト クラスを作成し、XUnit をインストールします。

/// <summary>
/// Testing Net Sockets..
/// </summary>
/// <author>Jeff Hansen</author>
public class SocketTests
{
    /// <summary>
    /// The local endpoint for the server. There's no place like 127.0.0.1
    /// </summary>
    private IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1337);

    /// <summary>
    /// Tests that a call to .Receive will return all unread data.
    /// </summary>
    /// <author>Jeff Hansen</author>
    [Fact]
    public void ReceiveWillReadAllUnreadData()
    {
        // We expect to receive double data.
        const string expected = "HELLOHELLO";
        const string dataToSend = "HELLO";

        // Start the server in a background task.
        // We do this because the receive call is blocking.
        var bgTask = Task.Factory.StartNew(() =>
        {
            var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                // Bind to the endpoint and start listening.
                server.Bind(endpoint);
                server.Listen(10); // 10 is max allowed connections in queue (I think)

                // Client connected, receive data
                var connectingClient = server.Accept();

                // So low-level we even get to allocate room for the data buffer manually.
                var buf = new byte[1024];

                // Hangs until data flows.
                var sz = connectingClient.Receive(buf); // This is the size of the data we just received.
                var data = Encoding.ASCII.GetString(buf, 0, sz); // We use the size to only grab what we need from the buffer.
                Console.WriteLine("Data received: {0}", data);

                // This is going to pass because we sent the data twice,
                // and the call to Receive would not be able to complete in time
                // for it to clear before more data becomes available.
                Assert.Equal(expected, data);

                /*
                 * BAM! Theory proven. We seriously had issues 
                 * because we didn't understand how it worked. This is why you usually end
                 * your transmission with a newline.
                 */

                Console.WriteLine("Server closing");
                server.Close();
            }
            catch (Exception e)
            {
                // Make sure we close the server.
                server.Close();
                throw;
            }
        });

        // Create a client socket and connect it to the server.
        // The server thread should have started it up by now.
        var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        client.Connect(endpoint);

        // Get the bytes of our string
        var buffer = Encoding.ASCII.GetBytes(dataToSend);

        // Send it out twice. This happens faster than the server will process it,
        // so the data is stacking up. You would THINK that .Receive would
        // simply return the first data sent to it, and the next time .Receive is called
        // return the next. But that's not how it works, apparently.
        client.Send(buffer);
        client.Send(buffer);

        // Wait for the server to finish whatever it's doing.
        try
        {
            // We give it 3000ms to complete.
            bgTask.Wait();
        }
        catch (AggregateException ag)
        {
            // Throw any esceptions that were thrown in the background thread.
            ag.Handle(ex => { throw ex; });
        }

        // Close the client socket.
        client.Close();
    }
}

データを改行で区切ることで解決しました。

楽しい事実: 私は TcpClient をいじっていましたが、ストリームに改行を書き込まないと、何も送信されず、そこでハングすることがわかりました。ただし、ローカルの問題だった可能性があります。それについて詳しく説明したい人はいますか?

于 2013-12-11T17:46:34.843 に答える