使数据接收/数据监听器保持活力的最佳方法是什么?

时间:2015-05-22 08:36:29

标签: c# async-await tcp

我的任务是通过TCP创建一个用于数据通信的客户端应用程序,我没有TCP数据通信编程的经验。我使用C#和VS2013。在我工作的公司里有一个黑盒服务器应用程序。我只知道它的ip,端口及其在数据通信上的行为:

  1. 它正在等待XML格式和UTF8编码的消息。其他格式或编码将被忽略。
  2. 一旦收到消息,就会将收到的消息作为确认发回。
  3. 服务器还可以向客户端发送另一条消息,而不先从客户端接收消息。
  4. 我已经实现了第1点和第2点(参见下面的代码),它们可以工作。但我很困惑(在第3点)如何使“等待和接收数据”部分成为一个循环,直到客户端应用程序关闭为止。提前感谢任何帮助和提示。< / p>

    using System;
    using System.Net.Sockets;
    using System.Text;
    
    namespace ConsoleTest01
    {
        public class Program
        {
            private const String _xmlHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
            private const String _xmlEndTag = "</xml>";
            private static TcpClient _client;
            private static NetworkStream _stream;
    
            static void Main(string[] args)
            {
                Connect("127.0.0.1", 4502, "<DummySettings></DummySettings>");
            }
    
            static void Connect(String server, Int32 port, String message)
            {
                try
                {
                    _client = new TcpClient(server, port);
    
                    // Prepare data
                    message = _xmlHeader + message + _xmlEndTag;
                    Byte[] data = UTF8Encoding.UTF8.GetBytes(message);
                    Byte[] suffix = UTF8Encoding.UTF8.GetBytes("\r\n");
                    Byte[] utf8Result = new byte[data.Length + suffix.Length];
                    Buffer.BlockCopy(data, 0, utf8Result, 0, data.Length);
                    Buffer.BlockCopy(suffix, 0, utf8Result, data.Length,      suffix.Length);
    
                    // Send data
                    if (_client != null) _stream = _client.GetStream();
                    _stream.Write(utf8Result, 0, utf8Result.Length);
                    Console.WriteLine("Sent: {0}", message);
    
                    // Waiting and receive data
                    var acknowledgement = new Byte[data.Length + suffix.Length];
                    String responseData = String.Empty;
                    Int32 bytes = _stream.Read(acknowledgement, 0,     acknowledgement.Length);
                    responseData = UTF8Encoding.UTF8.GetString(acknowledgement, 0, bytes);
                    Console.WriteLine("Received: {0}", responseData);
    
                    // In real life the close commands are not called here.
                    _stream.Close();
                    _client.Close();
                }
                catch (ArgumentNullException e)
                {
                     Console.WriteLine("ArgumentNullException: {0}", e);
                }
                catch (SocketException e)
                {
                     Console.WriteLine("SocketException: {0}", e);
                }
    
                Console.WriteLine("\n Press Enter to continue...");
                Console.Read();
            }
        }
    }
    

    编辑:有关我的代码的更多信息。

    使用上面的代码我可以向服务器发送消息获取服务器的确认,然后应用程序因设计而关闭,因为它只是我的实验代码。用户Prabhu正确理解我的案例。

    现在我想知道如果服务器向我的客户端发送另一条消息并且我的客户端可以处理消息,如何实现仅接收部分来处理这种情况。因此,接收部分应保持活动状态以监听传入消息,直到我的客户端应用程序关闭。它可能涉及背景线程(我没有经验)。一个示例代码(或其链接)将受到高度赞赏。

3 个答案:

答案 0 :(得分:2)

你需要围绕发送 - 接收对包裹一个循环:

while (true) {
 var data = ...;
 Send(data);
 var receivedData = Receive();
 Debug.Assert(data == receivedData);
}

就像那样。

由于没有两个操作同时发生,因此不必使用异步IO或线程。我特别建议不要使用非阻塞套接字和“select”方法,因为它在.NET 4.5上已经过时了。

您当前的代码已损坏,因为它假定读取操作将返回特定数量的字节。事实并非如此,请参阅文档。

使用BinaryReader轻松读取确切的字节数。

答案 1 :(得分:1)

您的应用程序要求调用非阻塞套接字。事件可以异步发生。

  1. 将数据发送到服务器
  2. 从服务器接收已发送数据和流程的响应
  3. 接收服务器自己的消息和流程。
  4. 您可以拥有一个专用线程,用于监视客户端和服务器上连接的套接字。您可以使用select机制进行监控。称之为socket-thread。将数据发送到服务器的线程 - 称之为工作线程。

    连接的套接字ID sd应与线程共享。当工作线程有数据要发送到服务器时,它应该在连接的sendsd。服务器的响应应该唤醒sd上的套接字线程轮询。它应该完全接收消息(应用程序应该注意识别完整的消息)并将数据传递给工作线程进行处理。

    类似地,当服务器消息(不响应)进入时,套接字线程将被唤醒。同样应该传递给工作线程。

    工作线程可以在事件循环中执行上面列出的操作。

答案 2 :(得分:0)

最后,我找到了问题的解决方案。我不确定这是否是最佳做法,但它适用于我的情况。如果有人有更好的想法来改进我的解决方案,请告诉我。

后台工作人员:

    private static void StartListening()
    {
        _dataLength = (_dataLength < (32 * 1024)) ? 32 * 1024 : _dataLength;

        while (!_shouldStop) // _shouldStop is a boolean private field
        {
            try
            {
                if (_stream.DataAvailable) // _stream is a NetworkStream
                {
                    Byte[] buffer = new Byte[_dataLength];
                    Int32 readBytes = _stream.Read(buffer, 0, buffer.Length);
                    // ResponseData is a property of type String
                    ResponseData = UTF8Encoding.UTF8.GetString(buffer, 0, readBytes);
                }
            }
            catch (IOException ioex)
            {
                // Do nothing since the client and socket are already closed.
            }
        }
    }

来电者:

    _shouldStop = false;
    var t = new Thread(MyClass.StartListening);
    t.IsBackground = true;
    t.Start();