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

C# 使数据接收/数据侦听器保持活动状态的最佳方法是什么?,c#,async-await,tcp,C#,Async Await,Tcp,我的任务是创建一个通过TCP进行数据通信的客户机应用程序,我没有TCP数据通信编程的经验。我使用C#和VS2013。在我工作的公司里有一个黑盒服务器应用程序。我只知道它的ip、端口和它在数据通信上的行为: 它正在等待XML格式和UTF8编码的消息。其他格式或编码将被忽略 一旦收到消息,它就会将收到的消息作为确认发送回 服务器还可以向客户端发送另一条消息,而不必首先从客户端接收消息 我已经实现了第1点和第2点(请参见下面的代码),它们可以工作。但是我很困惑(在第3点)如何使“等待并接收数据”部分进

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

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

    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();
            }
        }
    }
    
    使用系统;
    使用System.Net.Sockets;
    使用系统文本;
    名称空间控制台测试01
    {
    公共课程
    {
    私有常量字符串_xmlHeader=“”;
    私有常量字符串_xmlEndTag=“”;
    专用静态TcpClient\u客户端;
    私有静态网络流_流;
    静态void Main(字符串[]参数)
    {
    连接(“127.0.0.1”,4502,”;
    }
    静态void连接(字符串服务器、Int32端口、字符串消息)
    {
    尝试
    {
    _客户端=新的TcpClient(服务器、端口);
    //准备数据
    消息=_xmlHeader+消息+_xmlEndTag;
    Byte[]data=UTF8Encoding.UTF8.GetBytes(消息);
    字节[]后缀=UTF8Encoding.UTF8.GetBytes(“\r\n”);
    字节[]utf8Result=新字节[data.Length+后缀.Length];
    块复制(数据,0,utf8Result,0,数据.长度);
    块复制(后缀,0,utf8Result,数据.长度,后缀.长度);
    //发送数据
    如果(_client!=null)_stream=_client.GetStream();
    _stream.Write(utf8Result,0,utf8Result.Length);
    WriteLine(“发送:{0}”,消息);
    //等待并接收数据
    var确认=新字节[data.Length+后缀.Length];
    String responseData=String.Empty;
    Int32字节=_stream.Read(确认,0,确认.长度);
    responseData=UTF8Encoding.UTF8.GetString(确认,0,字节);
    WriteLine(“接收:{0}”,responseData);
    //在现实生活中,这里不调用close命令。
    _stream.Close();
    _client.Close();
    }
    捕获(e)
    {
    WriteLine(“ArgumentNullException:{0}”,e);
    }
    捕获(SocketException e)
    {
    WriteLine(“SocketException:{0}”,e);
    }
    Console.WriteLine(“\n按Enter键继续…”);
    Console.Read();
    }
    }
    }
    
    编辑:有关我的代码的更多信息

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


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

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

  • 向服务器发送数据
  • 从服务器接收发送数据和进程的响应
  • 接收服务器自己的消息和进程
  • 您可以有一个专用线程,该线程在连接的套接字上监视客户端和服务器。您可以使用
    选择
    机制进行监控。称之为套接字线程。将数据发送到服务器的线程-称之为工作线程

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

    类似地,当服务器消息(无响应)传入时,套接字线程将被唤醒。应将其传递给工作螺纹


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

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

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

    因为没有两个操作同时发生,所以没有必要使用异步IO或线程。我特别建议不要使用非阻塞套接字和“select”方法,因为它在.NET4.5的wait中已经过时了

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


    使用
    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.
                }
            }
        }
    

    如果您自己不确定,我们很难解释您的要求,但我最好的猜测是,您应该按需为3(而不是3)打开与客户的连接
        _shouldStop = false;
        var t = new Thread(MyClass.StartListening);
        t.IsBackground = true;
        t.Start();