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

UDP Client网络重连后调用Send触发SocketException(10022)问题排查

UDP Client网络重连后场景2发送失败的问题分析与解决

问题描述

我在两种UDP发送场景中遇到了差异问题:

  • 场景1:创建UdpClient后,每次调用Send()时指定远程主机地址发送数据
  • 场景2:创建UdpClient后先调用Connect()设置默认远程主机,后续调用Send()无需指定远程主机(官方文档说明:Connect方法通过指定的endPoint参数建立默认远程主机,建立后每次调用Send无需再指定远程主机)

两种场景的UdpClient创建逻辑基本一致(仅场景2会调用Connect),创建代码如下:

private static UdpClient CreateUdpClient(IPAddress localNetworkAdapter, IPEndPoint addressForReadingData)
{
    var client = new UdpClient();
    client.Client.ExclusiveAddressUse = false;
    client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    client.Client.Blocking = false;
    client.Client.Ttl = 10;
    client.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 10);
    client.Client.Bind(new IPEndPoint(localNetworkAdapter, 0));
    client.Connect(addressForReadingData);
    return client;
}

网络正常时两种场景均工作正常,但断开网络后重新连接:

  • 场景1可自动恢复正常发送
  • 场景2每次调用Send()都会抛出SocketException(10022 - 提供了无效参数),必须关闭并重建UdpClient才能恢复

测试代码可复现该行为(修改scenario变量切换场景):

  • scenario=false时:网络断开时控制台输出错误,重连后恢复输出“Sent”
  • scenario=true时:网络重连后控制台仍持续输出错误

测试代码:

using System.Net;
using System.Net.Sockets;

namespace UdpTester
{
    static class Program
    {
        static void Main(string[] args)
        {
            // scenario 1 -> false
            // scenario 2 -> true
            bool scenario = true;
            
            IPEndPoint remoteHost = new IPEndPoint(IPAddress.Parse("172.30.65.8"), 3000);
            IPAddress localNetworkAdapter = IPAddress.Parse("172.30.65.3");
            ushort udpMaxPacketSizeInBytes = 1400;

            Console.WriteLine("Starting UDP tester:");
            Console.WriteLine($"Remote host: {remoteHost}");
            Console.WriteLine($"Network adapter: {localNetworkAdapter}");
            Console.WriteLine($"Max packet size (Bytes): {udpMaxPacketSizeInBytes}");

            var udpClient = CreateClient(localNetworkAdapter, remoteHost, udpMaxPacketSizeInBytes, scenario);

            SendData(udpClient, remoteHost, scenario);
        }

        static UdpClient CreateClient(IPAddress localNetworkAdapter, IPEndPoint remoteHost, int bufferSize, bool scenario)
        {
            var client = new UdpClient();
            client.Client.ExclusiveAddressUse = false;
            client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            client.Client.SendBufferSize = bufferSize;
            client.Client.Blocking = false;
            client.Client.Ttl = 10;
            client.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 10);
            client.Client.Bind(new IPEndPoint(localNetworkAdapter, 0));
            if(scenario)
                client.Connect(remoteHost);
            return client;
        }

        static void SendData(UdpClient client, IPEndPoint remoteHost, bool scenario)
        {
            Console.WriteLine("\nSending data...");
            while (true)
            {
                try
                {
                    if(scenario)
                        client.Send(new byte[1], 1);
                    else
                        client.Send(new byte[1], 1, remoteHost);                        
                    Thread.Sleep(500);
                    Console.WriteLine("Sent");
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
            }
        }
    }
}

原因分析

  1. UDP是无连接协议,UdpClient.Connect()并非建立真正的TCP式连接,而是在底层socket上绑定了固定的远程地址,同时会过滤接收的数据包(只接收来自该地址的包)。
  2. 当网络断开后,底层socket会记录一个错误状态(比如路由不可达)。场景1每次调用Send(byte[], int, IPEndPoint)时,会重新解析目标地址的路由信息,相当于重置了socket的错误状态;而场景2调用无参数的Send()时,会复用之前绑定的远程地址和缓存的错误状态,此时即使网络恢复,socket仍认为目标地址不可用,从而抛出10022无效参数错误。
  3. 10022错误在这里的本质是:socket在错误状态下,使用绑定的默认地址发送数据时,底层无法找到有效的路由,导致参数校验失败。

解决建议

方案1:捕获10022错误后重建UdpClient

在异常捕获逻辑中,当检测到SocketException错误码为10022时,销毁旧的UdpClient并重新创建连接:

catch (SocketException ex)
{
    if (ex.ErrorCode == 10022 && scenario)
    {
        client.Close();
        client = CreateClient(localNetworkAdapter, remoteHost, udpMaxPacketSizeInBytes, scenario);
        Console.WriteLine("Reconnected UDP client");
    }
    else
    {
        Console.WriteLine(ex);
    }
}

注:直接调用client.Connect(remoteHost)可能无法彻底清除底层socket的错误状态,重建实例是更可靠的方式。

方案2:监听网络状态重置连接

通过NetworkChange.NetworkAvailabilityChanged事件监听网络状态变化,当网络恢复时自动重建UdpClient实例,避免手动处理异常逻辑。

方案3:统一使用指定地址的Send()

如果业务允许,放弃使用Connect()绑定默认地址的方式,统一采用场景1的发送逻辑,每次发送时指定远程主机地址,这样网络重连后无需额外处理即可自动恢复。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 07:05:04