C#套接字接收线程

时间:2013-05-11 21:50:28

标签: c# multithreading sockets

我有一个套接字连接,我通过这个套接字发送数据。我连接的服务器回答每次正确的数据发送。我收到了消息,所以我得到了收到的每条消息的答案。有时,服务器喜欢保留消息几秒钟,或者以不同的顺序发送。我的解决方案是生成一个线程并让它围绕一个接收函数旋转。但是,在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);
}

编辑,添加产生接收线程的功能。 (这是太长了,但是当我让愚蠢的事情发挥作用时,我计划让它变得漂亮):

    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");
    }

编辑: 我在发送操作后放了一个睡眠定时器,修复了我遇到的问题。 谢谢!

1 个答案:

答案 0 :(得分:0)

我认为我有类似的问题。

我使用Socket类发送JSON字符串,突然JSON.NET会抛出异常。我检查了字符串,我看到正在处理2个JSON(根)对象。然后我把一个最小的例子拼凑在一起,发现套接字会隐藏*任何未读的数据(未读=没有.Receive已被调用,而.Receive将清除存储)。

*藏匿:我不确定这是否是正确的术语。这对我来说很有意义。

这是我的测试。如果要运行它,请创建一个新的测试类并安装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,发现如果我没有给流写一个换行符,它就不会发送任何东西 - 它只会挂在那里。可能是一个本地化的问题。有人关心这个吗?