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

如何解决C#中TcpListener的“Address already in use”异常(端口复用场景)

问题:同一本地端口同时用于TCP客户端连接和监听时出现“Address already in use”异常

我需要用C#实现一套系统,功能如下:

  • 通过类方法Method1连接远程主机;
  • 在类方法Method2中每隔10秒向远程主机发送请求,维持步骤1建立的连接;
  • 在类方法Method3中监听步骤1、2发送请求时使用的同一本地端口。

但执行步骤3时,出现了“Address already in use”异常,以下是相关代码:

public static async Task Method1()
{
    TcpClient _client = new TcpClient();
    _client = new TcpClient();

    await _client.ConnectAsync("IPAddress", 1234);
    var localEndpoint = (IPEndPoint)_client.Client.LocalEndPoint;
    sourcePort = localEndpoint.Port;
    AppStatics.SourcePort = sourcePort;

    var _stream = _client.GetStream();
    await _stream.WriteAsync("...");
    Task.Run(() => Method2(_stream));
    Method3();
}

public static async Task Method2(NetworkStream stream)
{
    //Start trying to connect to the remote server here

    Console.WriteLine("Checking the connection with the USSD gateway...");
    var _shakeTimer = new System.Timers.Timer(10000);
    _shakeTimer.Elapsed += async (object source, ElapsedEventArgs e) => await SendNetworkConnectionCheck(stream);
    _shakeTimer.AutoReset = true;
    _shakeTimer.Enabled = true;

}

private static async Task<bool> SendNetworkConnectionCheck(NetworkStream stream)
{
    stream.Write("...");
}


public static void Method3()
{
    TcpListener listener = new TcpListener(IPAddress.Any, AppStatics.SourcePort);
    listener.ExclusiveAddressUse = false;
    listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    try
    {                
        listener.Stop();
        listener.Start();
        while (true)
        {
            TcpClient acceptTask =  listener.AcceptTcpClient();

            TcpClient incomingClient = acceptTask;

        }
    }
    catch (SocketException sex)
    {
        if (sex.SocketErrorCode == SocketError.AddressAlreadyInUse)
        {
            listener.Stop();
            listener = new TcpListener(IPAddress.Any, AppStatics.SourcePort);
            listener.ExclusiveAddressUse = false;
            listener.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            listener.Start();
        }else
        {                   
            Console.WriteLine(sex.InnerException?.Message ?? sex.Message);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("There was error while trying to listen/handle incoming message");
        string errMsg = ex.InnerException?.Message ?? ex.Message;
        Console.WriteLine(errMsg);
        Console.WriteLine(ex.StackTrace?.ToString() ?? ex.ToString());
    }
}
解决方案

1. 明确TCP端口复用的核心逻辑

TCP客户端连接建立后,本地端口会与远程IP+端口形成双向绑定,默认无法直接在该端口启动监听。ReuseAddress选项的作用是允许不同套接字绑定到同一端口,但需要客户端和监听端都正确配置该选项,且满足TCP协议的复用规则。

2. 修正客户端套接字配置

在Method1中创建TcpClient时,提前设置端口复用选项,确保后续监听可以复用该端口:

public static async Task Method1()
{
    TcpClient _client = new TcpClient();
    // 配置客户端套接字允许端口复用
    _client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    _client.Client.ExclusiveAddressUse = false;

    await _client.ConnectAsync("IPAddress", 1234);
    var localEndpoint = (IPEndPoint)_client.Client.LocalEndPoint;
    sourcePort = localEndpoint.Port;
    AppStatics.SourcePort = sourcePort;

    var _stream = _client.GetStream();
    await _stream.WriteAsync(Encoding.UTF8.GetBytes("..."));
    // 用_ = 避免未捕获任务警告
    _ = Task.Run(() => Method2(_stream));
    Method3();
}

3. 重构监听逻辑,正确配置套接字

放弃TcpListener的间接配置,直接使用Socket类来构建监听,确保复用选项在绑定前生效:

public static void Method3()
{
    using Socket listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    
    // 关键配置:允许端口复用 + 非独占地址
    listenerSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    listenerSocket.ExclusiveAddressUse = false;

    try
    {
        listenerSocket.Bind(new IPEndPoint(IPAddress.Any, AppStatics.SourcePort));
        listenerSocket.Listen(10); // 设置监听队列长度

        while (true)
        {
            using Socket incomingSocket = listenerSocket.Accept();
            // 处理 incomingSocket 的数据交互逻辑
        }
    }
    catch (SocketException sex)
    {
        if (sex.SocketErrorCode != SocketError.AddressAlreadyInUse)
        {
            Console.WriteLine($"Socket异常: {sex.Message}");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("监听/处理消息时出错");
        string errMsg = ex.InnerException?.Message ?? ex.Message;
        Console.WriteLine($"错误信息: {errMsg}");
        Console.WriteLine(ex.StackTrace ?? ex.ToString());
    }
}

4. 其他优化点

  • SendNetworkConnectionCheck方法中,stream.Write需传入字节数组,建议补充编码转换(如Encoding.UTF8.GetBytes("...")),同时使用异步写法await stream.WriteAsync保持一致性;
  • Method2中的定时器需注意生命周期,可通过将定时器变量提升为类成员避免被GC回收;
  • 移除Method3中重复创建TcpListener的无效重试逻辑,一次配置到位即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 18:32:28