我想为WCF服务调用创建一个新的net.tcp:// localhost:x / Service端点,并动态分配新的开放TCP端口。
我知道当我打开到给定服务器的连接时,TcpClient将分配一个新的客户端端口。
是否有一种简单的方法来查找.NET中的下一个打开的TCP端口?
我需要实际的数字,以便可以在上面构建字符串。0不起作用,因为我需要将该字符串传递给另一个进程,以便可以在该新通道上进行回调。
Answers:
这是我要找的东西:
static int FreeTcpPort()
{
TcpListener l = new TcpListener(IPAddress.Loopback, 0);
l.Start();
int port = ((IPEndPoint)l.LocalEndpoint).Port;
l.Stop();
return port;
}
Sleep(100)
之间var port = FreeTcpPort()
的免费端口和启动HttpListener。然后,我循环运行了8个相同的过程。我永远都无法达到比赛条件。我的轶事证据(Win 7)是,操作系统显然会在短暂的端口范围(数千个)中循环,然后再次出现。所以上面的代码片段应该没问题。
TcpListener myActualServer = new TcpListener(ipAddress, 0); myActualServer.Start(); return ((IPEndPoint)myActualServer.LocalEndpoint).Port;
或者OP在实际启动其侦听器之前获取端口是否很重要?
使用端口号0。TCP堆栈将分配下一个空闲端口。
endpoint.ListenUriMode
为即可Unique
。
该解决方案可与TheSeeker的公认答案相媲美。虽然我认为它更具可读性:
using System;
using System.Net;
using System.Net.Sockets;
private static readonly IPEndPoint DefaultLoopbackEndpoint = new IPEndPoint(IPAddress.Loopback, port: 0);
public static int GetAvailablePort()
{
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
socket.Bind(DefaultLoopbackEndpoint);
return ((IPEndPoint)socket.LocalEndPoint).Port;
}
}
... SocketType.Dgram, ProtocolType.Udp...
如果您只想提供一个起始端口,并让它返回下一个可用的TCP端口,请使用如下代码:
public static int GetAvailablePort(int startingPort)
{
var portArray = new List<int>();
var properties = IPGlobalProperties.GetIPGlobalProperties();
// Ignore active connections
var connections = properties.GetActiveTcpConnections();
portArray.AddRange(from n in connections
where n.LocalEndPoint.Port >= startingPort
select n.LocalEndPoint.Port);
// Ignore active tcp listners
var endPoints = properties.GetActiveTcpListeners();
portArray.AddRange(from n in endPoints
where n.Port >= startingPort
select n.Port);
// Ignore active UDP listeners
endPoints = properties.GetActiveUdpListeners();
portArray.AddRange(from n in endPoints
where n.Port >= startingPort
select n.Port);
portArray.Sort();
for (var i = startingPort; i < UInt16.MaxValue; i++)
if (!portArray.Contains(i))
return i;
return 0;
}
首先打开端口,然后将正确的端口号提供给其他进程。
否则,仍然有可能通过其他一些过程首先打开端口,而您仍然拥有另一个端口。
我从Selenium.WebDriver DLL找到了以下代码
命名空间:OpenQA.Selenium.Internal
类别:PortUtility
public static int FindFreePort()
{
int port = 0;
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
IPEndPoint localEP = new IPEndPoint(IPAddress.Any, 0);
socket.Bind(localEP);
localEP = (IPEndPoint)socket.LocalEndPoint;
port = localEP.Port;
}
finally
{
socket.Close();
}
return port;
}
如果要获得特定范围内的空闲端口以将其用作本地端口/端点,请执行以下操作:
private int GetFreePortInRange(int PortStartIndex, int PortEndIndex)
{
DevUtils.LogDebugMessage(string.Format("GetFreePortInRange, PortStartIndex: {0} PortEndIndex: {1}", PortStartIndex, PortEndIndex));
try
{
IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
IPEndPoint[] tcpEndPoints = ipGlobalProperties.GetActiveTcpListeners();
List<int> usedServerTCpPorts = tcpEndPoints.Select(p => p.Port).ToList<int>();
IPEndPoint[] udpEndPoints = ipGlobalProperties.GetActiveUdpListeners();
List<int> usedServerUdpPorts = udpEndPoints.Select(p => p.Port).ToList<int>();
TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();
List<int> usedPorts = tcpConnInfoArray.Where(p=> p.State != TcpState.Closed).Select(p => p.LocalEndPoint.Port).ToList<int>();
usedPorts.AddRange(usedServerTCpPorts.ToArray());
usedPorts.AddRange(usedServerUdpPorts.ToArray());
int unusedPort = 0;
for (int port = PortStartIndex; port < PortEndIndex; port++)
{
if (!usedPorts.Contains(port))
{
unusedPort = port;
break;
}
}
DevUtils.LogDebugMessage(string.Format("Local unused Port:{0}", unusedPort.ToString()));
if (unusedPort == 0)
{
DevUtils.LogErrorMessage("Out of ports");
throw new ApplicationException("GetFreePortInRange, Out of ports");
}
return unusedPort;
}
catch (Exception ex)
{
string errorMessage = ex.Message;
DevUtils.LogErrorMessage(errorMessage);
throw;
}
}
private int GetLocalFreePort()
{
int hemoStartLocalPort = int.Parse(DBConfig.GetField("Site.Config.hemoStartLocalPort"));
int hemoEndLocalPort = int.Parse(DBConfig.GetField("Site.Config.hemoEndLocalPort"));
int localPort = GetFreePortInRange(hemoStartLocalPort, hemoEndLocalPort);
DevUtils.LogDebugMessage(string.Format("Local Free Port:{0}", localPort.ToString()));
return localPort;
}
public void Connect(string host, int port)
{
try
{
// Create socket
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
var localPort = GetLocalFreePort();
// Create an endpoint for the specified IP on any port
IPEndPoint bindEndPoint = new IPEndPoint(IPAddress.Any, localPort);
// Bind the socket to the endpoint
socket.Bind(bindEndPoint);
// Connect to host
socket.Connect(IPAddress.Parse(host), port);
socket.Dispose();
}
catch (SocketException ex)
{
// Get the error message
string errorMessage = ex.Message;
DevUtils.LogErrorMessage(errorMessage);
}
}
public void Connect2(string host, int port)
{
try
{
// Create socket
var localPort = GetLocalFreePort();
// Create an endpoint for the specified IP on any port
IPEndPoint bindEndPoint = new IPEndPoint(IPAddress.Any, localPort);
var client = new TcpClient(bindEndPoint);
//client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); //will release port when done
// Connect to the host
client.Connect(IPAddress.Parse(host), port);
client.Close();
}
catch (SocketException ex)
{
// Get the error message
string errorMessage = ex.Message;
DevUtils.LogErrorMessage(errorMessage);
}
}
如果要在给定范围内找到下一个可用的TCP端口,这是一种简化的实现方法:
private int GetNextUnusedPort(int min, int max)
{
if (max < min)
throw new ArgumentException("Max cannot be less than min.");
var ipProperties = IPGlobalProperties.GetIPGlobalProperties();
var usedPorts =
ipProperties.GetActiveTcpConnections()
.Where(connection => connection.State != TcpState.Closed)
.Select(connection => connection.LocalEndPoint)
.Concat(ipProperties.GetActiveTcpListeners())
.Concat(ipProperties.GetActiveUdpListeners())
.Select(endpoint => endpoint.Port)
.ToArray();
var firstUnused =
Enumerable.Range(min, max - min)
.Where(port => !usedPorts.Contains(port))
.Select(port => new int?(port))
.FirstOrDefault();
if (!firstUnused.HasValue)
throw new Exception($"All local TCP ports between {min} and {max} are currently in use.");
return firstUnused.Value;
}