TCPClient async/await C#
TCPClient async/await C#
我有好几台设备。该程序必须不断地 ping 这些设备。
遇到问题 - 如果连接丢失,那么我的程序除了连接丢失前的第一个轮询外不会显示任何内容,如果我恢复连接,那么在 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++;
}
}
这是我添加的新代码。
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);
}
}
}
您误用了 TCP 连接的工作方式。当您执行 client.ConnectAsync()
时,操作系统将需要一段时间才能真正超时。您设置的 tcp.SendTimeout = 1000;
对操作系统管理的 ConnectAsync()
没有影响,可以是 20 秒。
所以在这种情况下发生的事情是您在连接超时和连接连接之前将服务器重新联机。
因此,除非想要等待 20 秒才能收到警报,否则您将需要 运行 另一个超时来取消挂起的 Connect() 并报告您处于离线状态。例如,如果您在 1 秒内没有收到回复,则报告离线。
此外,如果连接由于被主动拒绝而失败,您还需要处理该测试用例。拒绝一般意味着你的服务器已经启动,但是端口没有在监听。然而,它也可能是防火墙主动拒绝连接,在这种情况下,您不知道服务器是否已启动。
考虑以下实现端口基本 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);
}
}
我有好几台设备。该程序必须不断地 ping 这些设备。
遇到问题 - 如果连接丢失,那么我的程序除了连接丢失前的第一个轮询外不会显示任何内容,如果我恢复连接,那么在 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++;
}
}
这是我添加的新代码。
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);
}
}
}
您误用了 TCP 连接的工作方式。当您执行 client.ConnectAsync()
时,操作系统将需要一段时间才能真正超时。您设置的 tcp.SendTimeout = 1000;
对操作系统管理的 ConnectAsync()
没有影响,可以是 20 秒。
所以在这种情况下发生的事情是您在连接超时和连接连接之前将服务器重新联机。
因此,除非想要等待 20 秒才能收到警报,否则您将需要 运行 另一个超时来取消挂起的 Connect() 并报告您处于离线状态。例如,如果您在 1 秒内没有收到回复,则报告离线。
此外,如果连接由于被主动拒绝而失败,您还需要处理该测试用例。拒绝一般意味着你的服务器已经启动,但是端口没有在监听。然而,它也可能是防火墙主动拒绝连接,在这种情况下,您不知道服务器是否已启动。
考虑以下实现端口基本 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);
}
}