C# 如何正确清理TcpListener,以避免;“地址已在使用中”;错误?
在多次运行我的程序时,我不断得到一个已在使用的C# 如何正确清理TcpListener,以避免;“地址已在使用中”;错误?,c#,sockets,.net-core,C#,Sockets,.net Core,在多次运行我的程序时,我不断得到一个已在使用的SocketException:Address 最简单的例子: using System; using System.Net; using System.Net.Sockets; using System.Threading; namespace test { class Program { static TcpListener listener; static void Main(string[]
SocketException:Address
最简单的例子:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace test
{
class Program
{
static TcpListener listener;
static void Main(string[] args)
{
listener = new TcpListener(new IPEndPoint(IPAddress.Any, 3349));
listener.Start();
listener.BeginAcceptSocket(Accept, null);
Console.WriteLine("Started!");
// Simulate a random other client connecting, nc localhost 3349 does the same thing
var client = new TcpClient();
client.Connect("localhost", 3349);
Thread.Sleep(2000);
listener.Stop();
Console.WriteLine("Done!");
}
static void Accept(IAsyncResult result)
{
using(var socket = listener.EndAcceptSocket(result))
{
Console.WriteLine("Accepted socket");
Thread.Sleep(500);
socket.Shutdown(SocketShutdown.Both);
}
Console.WriteLine("Socket fully closed");
}
}
}
运行程序两次(dotnet Run
):第一次将正常完成,但第二次将失败,并显示“地址已在使用”
请注意,缺少的dispose of客户端
不是这里的问题——我可以使用nc localhost 3349
手动复制相同的错误
如何清理侦听器以避免出错
操作系统和.NET信息:
dotnet --version
2.1.103
lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 16.04.4 LTS
Release: 16.04
Codename: xenial
Windows上不存在此问题。使用mono时也不会出现这种情况,因此这似乎是特定于Microsoft的linux实现。这实际上是预期的行为。若要修复此错误,应将套接字选项设置为true
,前提是且仅当代码未在Windows上运行:
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
}
Windows和Linux(可能还有MacOS)之间的差异是由以下因素造成的:
- 在Windows上,您可以绑定到一个地址和IP,除非在同一地址/IP组合上有另一个连接处于活动状态
- 在Linux上,您可以绑定到一个地址,除非同一地址/IP组合上有任何其他连接。这意味着,如果仍有连接处于TIME_WAIT或CLOSE_WAIT状态,则无法绑定
如中所述,SO_REUSEADDR
可用于放松Linux上的这些限制
Mono试图通过将SO\u REUSEADDR
设置为true
来模拟Windows的行为。官方的.NET核心运行时不这样做,因此会在Linux上导致“地址已在使用”错误
然而,这并不意味着总是将设置为\u REUSEADDR
是安全的!在Windows上,SO_REUSEADDR
的意思稍有不同():
具有SO_REUSEADDR的套接字始终可以绑定到完全相同的源
地址和端口作为已绑定的套接字,即使另一个套接字
绑定时未设置此选项。这种行为是错误的
有些危险,因为它允许应用程序“窃取”
另一个应用程序的已连接端口
因此,只能在非Windows系统上设置SO\u REUSEADDR
。一个解决方法可能是将设置为true
@itsme86谢谢!我还没想到。你知道这个选择是否有副作用吗?(只要一个程序的实例不超过一个)过去我自己用过,没有副作用。如果有,我不知道。在服务器中使用是相当标准的,所以\u REUSEADDR
。使用它产生问题的可能性实际上很小。这里首先解释了问题的原因: