简体   繁体   English

TCPClient 异步/等待 C#

[英]TCPClient async/await C#

I have several devices.我有几个设备。 The program must constantly ping these devices.该程序必须不断地 ping 这些设备。

Faced a problem - if the connection is lost, then my program does not display anything except the first poll before the connection is lost, and if I restore the connection, then after 15 seconds the program will start to output data.遇到一个问题——如果连接丢失,那么我的程序除了在连接丢失之前的第一次轮询外不显示任何内容,如果我恢复连接,那么程序将在 15 秒后开始输出数据。

public async Task Start(string ip)
    {
        textBox1.AppendText("Begin");
        textBox1.AppendText("\r\n");
        Stopwatch watch = new Stopwatch();

        int i = 0;

        while (true)
        {
            watch.Restart();
            using (TcpClient tcp = new TcpClient())
            {
                tcp.SendTimeout = 1000;
                try
                {
                    await tcp.ConnectAsync("192.168.127.23", 10001);
                }
                catch (SocketException)
                {
                    Debug.Assert(!tcp.Connected);
                }

                watch.Stop();
                if (tcp.Connected)
                {
                    textBox1.AppendText(i.ToString() + ") " + watch.ElapsedMilliseconds.ToString() + " ms");
                    textBox1.AppendText("\r\n");
                }
                else
                {
                    textBox1.AppendText(string.Format("{0}) Offline", i));
                }
            }

            await Task.Delay(1000);
            i++;
        }
    }

This is a new code, with my additions.这是一个新代码,有我的补充。

public async Task Start(string ip)
    {   
        while (true)
        {
            for (int i = 0; i < devicesListActivity.Count; i++)
            {
                devicesListActivity[i].DevicesList.DevicesTotalPing++;

                string ipAdresDevice = devicesListActivity[i].DevicesList.DevicesName;
                int portDevice = devicesListActivity[i].DevicesList.DevicesPort;
                int activeDevice = devicesListActivity[i].DevicesList.DevicesActiv;
                int imageDevice = devicesListActivity[i].DevicesList.DevicesImage;
                int sendTimeDevice = devicesListActivity[i].DevicesList.DevicesTimeSend;
                int respTimeDevice = devicesListActivity[i].DevicesList.DevicesTimeResp;

                var cts = new CancellationTokenSource(sendTimeDevice);
                var ct = cts.Token;

                var t = await Task.Run<ServerStatus>(() =>
                {
                    try
                    {
                        using (TcpClient client = new TcpClient())
                        {
                            client.ConnectAsync(ipAdresDevice, portDevice).Wait(sendTimeDevice);
                            ct.ThrowIfCancellationRequested();
                            client.Close();
                            return ServerStatus.Available;
                        }
                    }
                    catch (AggregateException ex) when (ex.InnerException.GetType() == typeof(SocketException))
                    {
                        if (((SocketException)ex.InnerException).SocketErrorCode == SocketError.ConnectionRefused)
                            return ServerStatus.Refused;
                        else
                        {
                            throw new Exception("Server did not respond");
                        }

                    }
                    catch (OperationCanceledException)
                    {
                        return ServerStatus.TimeOut;
                    }
                }, ct);

                switch (t)
                {
                    case ServerStatus.Available:
                        devicesListActivity[i].DevicesList.DevicesSuccessPing++;
                        textBox1.AppendText($"{DateTime.Now.ToString()} Server available" + " " + ipAdresDevice + string.Format(" [{0}/{1}]", devicesListActivity[i].DevicesList.DevicesSuccessPing, devicesListActivity[i].DevicesList.DevicesTotalPing) + " " + System.Math.Round((double)(devicesListActivity[i].DevicesList.DevicesSuccessPing / devicesListActivity[i].DevicesList.DevicesTotalPing * 100)) +" %");
                        textBox1.AppendText("\r\n");
                        break;
                    case ServerStatus.Refused:
                        textBox1.AppendText($"{DateTime.Now.ToString()} Server refused connection." + " " + ipAdresDevice + string.Format(" [{0}/{1}]", devicesListActivity[i].DevicesList.DevicesSuccessPing, devicesListActivity[i].DevicesList.DevicesTotalPing) + " " + System.Math.Round((double)(devicesListActivity[i].DevicesList.DevicesSuccessPing / devicesListActivity[i].DevicesList.DevicesTotalPing * 100)) + " %");
                        textBox1.AppendText("\r\n");
                        break;
                    case ServerStatus.TimeOut:
                        textBox1.AppendText($"{DateTime.Now.ToString()} Server did not respond." + " " + ipAdresDevice + string.Format(" [{0}/{1}]", devicesListActivity[i].DevicesList.DevicesSuccessPing, devicesListActivity[i].DevicesList.DevicesTotalPing) + " " + System.Math.Round((double)(devicesListActivity[i].DevicesList.DevicesSuccessPing / devicesListActivity[i].DevicesList.DevicesTotalPing * 100)) + " %");
                        textBox1.AppendText("\r\n");
                        break;
                }

                // Wait 1 second before trying the test again
                await Task.Delay(1000);
            }
        }
    }

You're misusing the way that TCP Connect works.您正在滥用 TCP Connect 的工作方式。 When you do a client.ConnectAsync() the operating system will take a period of time to actually time out.当您执行client.ConnectAsync() ,操作系统将需要一段时间才能实际超时。 Your setting of tcp.SendTimeout = 1000;您设置的tcp.SendTimeout = 1000; has no effect on the ConnectAsync() which is managed by the operating system which can be 20 seconds.对由操作系统管理的ConnectAsync()没有影响,它可以是 20 秒。

So what's happening in this case is that you're bringing the server back on line before the connect has timed out and the connect connects.因此,在这种情况下发生的情况是,您在连接超时和连接连接之前将服务器重新联机。

So unless want to wait 20 seconds to be alerted, you're going to need to run another timeout to cancel the pending Connect() and report that you are offline.因此,除非想等待 20 秒才能收到警报,否则您将需要运行另一个超时来取消挂起的 Connect() 并报告您处于离线状态。 Eg, if you don't get a response in 1 second, the report offline.例如,如果您在 1 秒内没有得到响应,则报告离线。

Also if the connection fails due to it being actively refused you'll need to handle that test case as well.此外,如果连接由于被主动拒绝而失败,您还需要处理该测试用例。 Refused generally means that your server is up, but the port is not listening.拒绝通常意味着您的服务器已启动,但端口未在侦听。 However it could also be a firewall actively refusing the connection, in which case you don't know if the server is up.但是,也可能是防火墙主动拒绝连接,在这种情况下,您不知道服务器是否已启动。

Consider the following code example which achieves basic TCP monitoring of a port:考虑以下实现端口基本 TCP 监控的代码示例:

private async void btnTest_Click(object sender, EventArgs e)
{
    int timeOut = 2000;

    while (true)
    {
        using (TcpClient client = new TcpClient())
        {
            var ca = client.ConnectAsync("127.0.0.1", 9999);
            await Task.WhenAny(ca, Task.Delay(timeOut));
            client.Close();
            if (ca.IsFaulted || !ca.IsCompleted)
                listBox1.Items.Add($"{DateTime.Now.ToString()} Server offline.");
            else
                listBox1.Items.Add($"{DateTime.Now.ToString()} Server available.");
        }
        // Wait 1 second before trying the test again
        await Task.Delay(1000);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM