You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在IP探测任务成功连接后终止任务或使用取消令牌?

IP探测任务成功时终止所有任务的实现方案

我有一个IP探测方法,通过TcpClient测试每个IP地址,当任意IP连接成功时,我会在内存中设置WebServerIp,此时任务即视为成功完成。请问能否在我的现有代码中实现该逻辑?或者有没有办法在任务“成功”时使用取消令牌?

原始代码

public async Task<bool> DiscoverDeviceAsync()
{
    var ips = GeneralService.GetIPAddress();
    PhoneIp = string.Join(",", ips.Select(x => x.ToString()));

    await Task.Delay(1000);

    var tasks = new List<Task>();

    foreach (IPAddress ip in ips)
    {
        byte[] ipBytes = ip.GetAddressBytes();

        for (int i = 0; i <= 254; i++)
        {
            ipBytes[3] = (byte)i;
            IPAddress currentIPAddress = new(ipBytes);
            tasks.Add(ScanIPAddress(currentIPAddress, 9431));
        }
    }

    await Task.WhenAny(tasks);

    return WebServerIp != "";
}

async Task ScanIPAddress(IPAddress ipAddress, int port)
{
    try
    {
        using TcpClient tcpClient = new()
        {
            SendTimeout = 100,
            ReceiveTimeout = 100,
        };

        await tcpClient.ConnectAsync(ipAddress, port);

        WebServerIp = ipAddress.ToString();
    }
    catch
    {
        throw new Exception($"No device found at address: {ipAddress}");
    }
}

现有代码问题分析

  1. ScanIPAddress捕获异常后直接抛出,可能导致Task.WhenAny优先捕获失败任务,而非等待成功任务完成
  2. 无取消机制,找到目标IP后其他扫描任务仍会继续执行,浪费系统资源
  3. Task.Delay(1000)属于无意义延迟,建议移除

修改后的实现(带取消令牌)

private CancellationTokenSource _cts;
public string WebServerIp { get; private set; } = string.Empty;

public async Task<bool> DiscoverDeviceAsync()
{
    WebServerIp = string.Empty;
    var ips = GeneralService.GetIPAddress();
    PhoneIp = string.Join(",", ips.Select(x => x.ToString()));

    _cts = new CancellationTokenSource();
    var tasks = new List<Task>();

    foreach (IPAddress ip in ips)
    {
        byte[] ipBytes = ip.GetAddressBytes();
        for (int i = 0; i <= 254; i++)
        {
            ipBytes[3] = (byte)i;
            IPAddress currentIPAddress = new(ipBytes);
            tasks.Add(ScanIPAddress(currentIPAddress, 9431, _cts.Token));
        }
    }

    try
    {
        var completedTask = await Task.WhenAny(tasks);
        // 如果任务成功完成,触发取消终止其他任务
        if (completedTask.Status == TaskStatus.RanToCompletion)
        {
            _cts.Cancel();
        }
    }
    finally
    {
        // 等待所有任务结束(忽略取消异常),避免后台任务残留
        await Task.WhenAll(tasks.Select(t => t.ContinueWith(_ => { })));
        _cts.Dispose();
    }

    return !string.IsNullOrEmpty(WebServerIp);
}

async Task ScanIPAddress(IPAddress ipAddress, int port, CancellationToken token)
{
    // 先检查令牌状态,避免无效操作
    token.ThrowIfCancellationRequested();

    try
    {
        using TcpClient tcpClient = new()
        {
            SendTimeout = 100,
            ReceiveTimeout = 100,
        };

        // 使用支持取消的ConnectAsync重载
        await tcpClient.ConnectAsync(ipAddress, port, token);

        // 连接成功后设置IP并触发取消
        WebServerIp = ipAddress.ToString();
        _cts.Cancel();
    }
    catch (OperationCanceledException)
    {
        // 取消异常无需处理
    }
    catch (Exception)
    {
        // 连接失败异常直接忽略,不向外抛出
    }
}

关键改动说明

  • 新增CancellationTokenSource,用于在成功时终止所有剩余扫描任务
  • ScanIPAddress接收取消令牌,在方法开头检查令牌状态,避免无效操作;连接时使用支持取消的ConnectAsync重载
  • 连接成功后立即触发取消,终止其他任务
  • 在finally块中等待所有任务结束,确保资源被正确清理,避免后台任务泄漏
  • 移除无意义的延迟,不再抛出连接失败异常,避免干扰Task.WhenAny的判断

内容的提问来源于stack exchange,提问作者Hickori

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 17:05:28