使数据接收/数据侦听器保持活动状态的最佳方法是什么?

What is the best approach to make Data Receive / Data Listener keeps alive?

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

  1. 它正在等待 XML 格式和 UTF8 编码的消息。其他格式或编码将被忽略。
  2. 一旦收到消息,它就会将收到的消息作为确认发送回去。
  3. 服务器也可以在不先从客户端接收消息的情况下向客户端发送另一条消息。

我已经实现了第 1 点和第 2 点(请参阅下面我的代码),它们有效。但是我很困惑(在第 3 点)如何使 "waiting and receive data" 部分成为一个循环,在客户端应用程序关闭之前一直保持活动状态。 在此先感谢您的帮助和提示.

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 正确理解了我的情况。

现在我想知道如何实现只接收部分来处理服务器向我的客户端发送另一条消息并且我的客户端可以处理该消息的情况。因此,接收部分应该保持活动状态以侦听传入消息,直到我的客户端应用程序关闭。它可能涉及后台线程(我没有经验)。非常感谢示例代码(或 link)。

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

  1. 向服务器发送数据
  2. 接收服务器对发送数据的响应并处理
  3. 接收服务器自己的消息和进程。

您可以有一个专用线程来监视连接的套接字b/w 客户端和服务器。您可以使用 select 机制进行监控。称之为套接字线程。将数据发送到服务器的线程 - 称之为工作线程。

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

同理,socket-thread会在服务器消息(无响应)进来时被唤醒,同样传递给工作线程。

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

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

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

就像那样。

由于不会同时发生两个操作,因此没有必要使用异步 IO 或线程。我特别建议不要使用非阻塞套接字和 "select" 方法,因为在带有 await.

的 .NET 4.5 上这已经过时了

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

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

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

后台工作者:

    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();