【问题标题】:C# Async TcpClient connectionC# 异步 TcpClient 连接
【发布时间】:2020-11-26 01:01:56
【问题描述】:

我正在尝试使用 TCP 客户端与服务器通信。但是要通信,有连接规则:

  1. 每当阅读器要开始通信时,必须在其输出上放置标记 1。
  2. 服务器将此信号视为连接请求指示,仅在稳定 1 秒后有效。
  3. 一旦连接请求被接受,服务器开始发送 ENQ 信号字符。 (ENQ = 05 十六进制)

我想我需要使用一些“睡眠”功能 1 秒并发送 1 作为标记。所以我实现了以下示例:

public void Initialize(string ip, int port)
{
    try
    {
        tcpClient = new TcpClient(ip, port);

        if (tcpClient.Connected)
            Console.WriteLine("Connected to: {0}:{1}", ip, port);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        Initialize(ip, port);
    }
}

public void BeginRead()
{
    var buffer = new byte[4096];
    NetworkStream ns = tcpClient.GetStream();
    
    ns.ReadTimeout = 1000;
    ns.BeginRead(buffer, 0, 9, EndRead, buffer);                
}

class Program
{
    static void Main(string[] args)
    {
        var client = new Client();
        client.Initialize("192.168.0.250", 2180);

        client.BeginRead();     
        Console.ReadLine();
    }
}

当我运行此代码时,显示消息:“已连接到 192.168.0.250”。现在按照规则,我需要从服务器接收 ENQ (05 Hexa) 信号。我如何接收这个信号?

【问题讨论】:

标签: c# asynchronous tcpclient


【解决方案1】:

我测试了另一个示例,这是我在 Stack 上找到的。并使用 TcpClient 和 Socket 成功连接。例子是这样的:

namespace test3
{
    class Program
    {
        private static void _TestSOCode()
        {
            using (var tcp = new TcpClient())
            {
                var c = tcp.BeginConnect(IPAddress.Parse("192.168.0.250"), 2180, null, null);
                var success = c.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1));

                if (!success)
                {
                    Console.WriteLine("Before cleanup");
                    tcp.Close();
                    tcp.EndConnect(c);
                    Console.WriteLine("After cleanup");
                    throw new Exception("Failed to connect.");
                }
            }
        }

        private static void _TestWithTcpClient()
        {
            TcpClient client = new TcpClient();
            object o = new object();

            Console.WriteLine("connecting TcpClient...");
            client.BeginConnect("192.168.0.250", 2180, asyncResult =>
            {
                Console.WriteLine("connect completed");

                try
                {
                    client.EndConnect(asyncResult);
                    Console.WriteLine("client connected");
                }
                catch (NullReferenceException)
                {
                    Console.WriteLine("client closed before connected: NullReferenceException");
                }
                catch (ObjectDisposedException)
                {
                    Console.WriteLine("client closed before connected: ObjectDisposedException");
                }

                lock (o) Monitor.Pulse(o);
            }, null);

            Thread.Sleep(1000);

            Stopwatch sw = Stopwatch.StartNew();
            client.Close();

            lock (o) Monitor.Wait(o);
            Console.WriteLine("close took {0:0.00} seconds", sw.Elapsed.TotalSeconds);
            Console.WriteLine();
        }

        private static void _TestWithSocket()
        {
            Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
            object o = new object();

            Console.WriteLine("connecting Socket...");
            socket.BeginConnect("192.168.0.250", 2180, asyncResult =>
            {
                Console.WriteLine("connect completed");

                try
                {
                    socket.EndConnect(asyncResult);
                    Console.WriteLine("socket connected");
                }
                catch (ObjectDisposedException)
                {
                    Console.WriteLine("socket closed before connected");
                }

                lock (o) Monitor.Pulse(o);
            }, null);

            Thread.Sleep(1000);

            Stopwatch sw = Stopwatch.StartNew();
            socket.Close();

            lock (o) Monitor.Wait(o);
            Console.WriteLine("close took {0:0.00} seconds", sw.Elapsed.TotalSeconds);
            Console.WriteLine();
        }

        static void Main(string[] args)
        {
            _TestWithSocket();
            _TestWithTcpClient();

            try
            {
                _TestSOCode();
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception: " + e);
            }
        }
    }
}

现在我需要弄清楚如何读取服务器响应。规则#3:

  1. 一旦连接请求被接受,服务器开始发送 ENQ 信号字符。 (ENQ = 05 十六进制)

【讨论】:

    【解决方案2】:

    他们使用Begin...End... 模式的方式有点偏离。我将为您提供一个基本示例,说明如何进行此操作。

    你在这里做什么:

    tcpClient = new TcpClient(ip, port);
    
    if (tcpClient.Connected)
        Console.WriteLine("Connected to: {0}:{1}", ip, port);
    

    错了。你永远不会调用Connect... 方法。所以,没有任何关联。

    以下是对 TcpClient 对象的典型 CRUD 操作:

    public sealed class TcpClientTest
    {
        private readonly TcpClient _tcpClient = new TcpClient();
    
        public void Connect(string host, int port, Action endConnect)
        {
            _tcpClient.BeginConnect(host, port, asyncResult =>
            {
                _tcpClient.EndConnect(asyncResult);
                endConnect?.Invoke();
            }, null);
        }
    
        public void Read(byte[] buffer, Action<byte[], int> endRead)
        {
            _tcpClient.GetStream().BeginRead(buffer, 0, buffer.Length, asyncResult =>
            {
                var bytesRead = _tcpClient.GetStream().EndRead(asyncResult);
                endRead?.Invoke(buffer, bytesRead);
            }, null);
        }
    
        public void Write(byte[] buffer, Action endWrite)
        {
            _tcpClient.GetStream().BeginWrite(buffer, 0, buffer.Length, asyncRsult =>
            {
                _tcpClient.GetStream().EndWrite(asyncRsult);
                endWrite?.Invoke();
            }, null);
        }
    
        public void Disconnect()
        {
            _tcpClient.Close();
        }
    }
    

    你可以这样调用这段代码:

    Console.WriteLine("Connecting...");
    var tcp = new TcpClientTest();
    tcp.Connect("www.example.com", 80, () =>
    {
        Console.WriteLine("We are connected... Sending request...");
        var str = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n";
        tcp.Write(Encoding.UTF8.GetBytes(str), () =>
        {
            Console.WriteLine("Data sent. Waiting for data to come back...");
            var bytes = new byte[2048];
            tcp.Read(bytes, (buffer, read) =>
            {
                var data = Encoding.UTF8.GetString(buffer, 0, read);
                Console.WriteLine($"Data Read: {data}");
    
                Console.WriteLine("Closing connection");
                tcp.Disconnect();
    
                Console.WriteLine("Done.");
            });
        });
    })
    

    如果您不使用Begin...End... 方法,而是使用...Async 方法,例如ConnectAsyncWriteAsyncReadAsync 等方法,事情会变得容易一些。

    这需要了解async/await 模式,起初这很复杂,但超时成为您可能使用过的最有用的模式之一。

    【讨论】:

    • 嗨,安迪,感谢您的帮助。我测试了你的例子,但它没有连接。我只需要阅读,所以我评论了“tcp.Write”部分..我添加了“Console.Readline();”在消息“我们已连接”之后。但是消息没有出现。我试图读取的服务器是一个电能表……它使用一种名为 Bisync (IBM) 的技术,类似于 Modbus。
    • @JoãoSoares -- 有我可以测试的服务器/端口吗?
    • @JoãoSoares -- 我不能只知道端口就进行测试,我需要一个服务器来连接,这样我才能测试或查看它是如何工作的。
    • 嗯......不,不幸的是......它是一个电能表,我只有局域网访问权限。但我有传输细节: - 速度:9600 波特 +/- 2%; - 类型:异步; - 模式:双向; - 字符:1个起始位,8个数据位,1个停止位; - 块大小:53 字节; - 块之间的时间:1 整秒;
    • 我已经设法连接,使用我的第二个答案和 BeginConnect 方法。现在我需要读取并打印服务器返回。搜索 BeginConnect,我想我需要实现 IAsyncResult。如何使用 IAsyncResult 获取 BeginConnect 方法的返回值?
    猜你喜欢
    • 2017-05-20
    • 1970-01-01
    • 1970-01-01
    • 2017-05-21
    • 2011-11-24
    • 2015-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多