diff --git a/src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor b/src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor
index 17335a2e08d..f7d6208f579 100644
--- a/src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor
+++ b/src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor
@@ -66,7 +66,7 @@ private async Task CreateClient()
}
-
内置数据库处理器
+内置数据处理器
FixLengthDataPackageHandler 固定长度数据处理器 即每个通讯包都是固定长度
diff --git a/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs b/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs
index a456898b43e..785afffb34a 100644
--- a/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs
+++ b/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+using System.Runtime.Versioning;
using System.Text;
namespace BootstrapBlazor.Components;
@@ -10,6 +11,7 @@ namespace BootstrapBlazor.Components;
///
/// 扩展方法类
///
+[UnsupportedOSPlatform("browser")]
public static class ITcpSocketClientExtensions
{
///
@@ -30,4 +32,20 @@ public static ValueTask SendAsync(this ITcpSocketClient client, string con
var buffer = encoding?.GetBytes(content) ?? Encoding.UTF8.GetBytes(content);
return client.SendAsync(buffer, token);
}
+
+ ///
+ /// Establishes an asynchronous connection to the specified host and port.
+ ///
+ /// The TCP socket client to which the content will be sent. Cannot be .
+ /// The hostname or IP address of the server to connect to. Cannot be null or empty.
+ /// The port number on the server to connect to. Must be a valid port number between 0 and 65535.
+ /// An optional to cancel the connection attempt. Defaults to if not provided.
+ /// A task that represents the asynchronous operation. The task result is if the connection
+ /// is successfully established; otherwise, .
+ public static ValueTask ConnectAsync(this ITcpSocketClient client, string ipString, int port, CancellationToken token = default)
+ {
+ var endPoint = Utility.ConvertToIpEndPoint(ipString, port);
+ return client.ConnectAsync(endPoint, token);
+ }
}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs b/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs
index 33679cb473b..65d8b57f20a 100644
--- a/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs
+++ b/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs
@@ -12,7 +12,7 @@
namespace BootstrapBlazor.Components;
[UnsupportedOSPlatform("browser")]
-class DefaultTcpSocketClient : ITcpSocketClient
+class DefaultTcpSocketClient(IPEndPoint endPoint) : ITcpSocketClient
{
private TcpClient? _client;
private IDataPackageHandler? _dataPackageHandler;
@@ -21,7 +21,7 @@ class DefaultTcpSocketClient : ITcpSocketClient
public bool IsConnected => _client?.Connected ?? false;
- public IPEndPoint LocalEndPoint { get; set; }
+ public IPEndPoint LocalEndPoint { get; set; } = endPoint;
[NotNull]
public ILogger? Logger { get; set; }
@@ -30,29 +30,11 @@ class DefaultTcpSocketClient : ITcpSocketClient
public Func, ValueTask>? ReceivedCallBack { get; set; }
- public DefaultTcpSocketClient(string host, int port = 0)
- {
- LocalEndPoint = new IPEndPoint(GetIPAddress(host), port);
- }
-
- private static IPAddress GetIPAddress(string host) => host.Equals("localhost", StringComparison.OrdinalIgnoreCase)
- ? IPAddress.Loopback
- : IPAddress.TryParse(host, out var ip) ? ip : IPAddressByHostName;
-
- [ExcludeFromCodeCoverage]
- private static IPAddress IPAddressByHostName => Dns.GetHostAddresses(Dns.GetHostName(), AddressFamily.InterNetwork).FirstOrDefault() ?? IPAddress.Loopback;
-
public void SetDataHandler(IDataPackageHandler handler)
{
_dataPackageHandler = handler;
}
- public ValueTask ConnectAsync(string host, int port, CancellationToken token = default)
- {
- var endPoint = new IPEndPoint(GetIPAddress(host), port);
- return ConnectAsync(endPoint, token);
- }
-
public async ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default)
{
var ret = false;
diff --git a/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketFactory.cs b/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketFactory.cs
index 8f52e380cd6..d6573cd02fa 100644
--- a/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketFactory.cs
+++ b/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketFactory.cs
@@ -6,6 +6,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;
+using System.Net;
using System.Runtime.Versioning;
namespace BootstrapBlazor.Components;
@@ -19,7 +20,8 @@ public ITcpSocketClient GetOrCreate(string host, int port = 0)
{
return _pool.GetOrAdd($"{host}:{port}", key =>
{
- var client = new DefaultTcpSocketClient(host, port)
+ var endPoint = Utility.ConvertToIpEndPoint(host, port);
+ var client = new DefaultTcpSocketClient(endPoint)
{
Logger = provider.GetService>()
};
diff --git a/src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs b/src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs
index 339ad56c42a..12a46ee8299 100644
--- a/src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs
+++ b/src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs
@@ -44,17 +44,6 @@ public interface ITcpSocketClient : IDisposable
/// The handler responsible for processing data packages. Cannot be null.
void SetDataHandler(IDataPackageHandler handler);
- ///
- /// Establishes an asynchronous connection to the specified host and port.
- ///
- /// The hostname or IP address of the server to connect to. Cannot be null or empty.
- /// The port number on the server to connect to. Must be a valid port number between 0 and 65535.
- /// An optional to cancel the connection attempt. Defaults to if not provided.
- /// A task that represents the asynchronous operation. The task result is if the connection
- /// is successfully established; otherwise, .
- ValueTask ConnectAsync(string host, int port, CancellationToken token = default);
-
///
/// Establishes an asynchronous connection to the specified endpoint.
///
diff --git a/src/BootstrapBlazor/Utils/Utility.cs b/src/BootstrapBlazor/Utils/Utility.cs
index 3cfdea287a8..2c9ff091d20 100644
--- a/src/BootstrapBlazor/Utils/Utility.cs
+++ b/src/BootstrapBlazor/Utils/Utility.cs
@@ -8,7 +8,10 @@
using System.ComponentModel;
using System.Data;
using System.Linq.Expressions;
+using System.Net;
+using System.Net.Sockets;
using System.Reflection;
+using System.Runtime.Versioning;
namespace BootstrapBlazor.Components;
@@ -898,4 +901,63 @@ static Expression> CreateLambda(Type
///
///
public static IStringLocalizer? CreateLocalizer(Type type) => CacheManager.CreateLocalizerByType(type);
+
+ ///
+ /// Converts a string representation of an IP address or hostname into an object.
+ ///
+ /// This method handles common special cases for IP address strings, such as "localhost" and
+ /// "any". For other inputs, it attempts to parse the string as an IP address using . If parsing fails, the method resolves the input as a
+ /// hostname.
+ /// A string containing the IP address or hostname to convert. Special values include:
+ /// - "localhost" returns the loopback address ().
- "any" returns the wildcard address
+ /// ().
For other values, the method attempts to parse the
+ /// string as an IP address or resolve it as a hostname.
+ /// An object representing the parsed or resolved IP address. If the input cannot be parsed
+ /// or resolved, the method returns a default IP address.
+ [UnsupportedOSPlatform("browser")]
+ public static IPAddress ConvertToIPAddress(string ipString)
+ {
+ if (string.IsNullOrEmpty(ipString))
+ {
+ throw new ArgumentNullException(nameof(ipString), "IP address cannot be null or empty.");
+ }
+
+ if (ipString.Equals("localhost", StringComparison.OrdinalIgnoreCase))
+ {
+ return IPAddress.Loopback;
+ }
+ if (ipString.Equals("any", StringComparison.OrdinalIgnoreCase))
+ {
+ return IPAddress.Any;
+ }
+
+ return IPAddress.TryParse(ipString, out var ip) ? ip : IPAddressByHostName;
+ }
+
+ [ExcludeFromCodeCoverage]
+
+ [UnsupportedOSPlatform("browser")]
+ private static IPAddress IPAddressByHostName => Dns.GetHostAddresses(Dns.GetHostName(), AddressFamily.InterNetwork).FirstOrDefault() ?? IPAddress.Loopback;
+
+ ///
+ /// Converts a string representation of an IP address and a port number into an instance.
+ ///
+ /// This method is not supported on browser platforms.
+ /// The string representation of the IP address. Must be a valid IPv4 or IPv6 address.
+ /// The port number associated with the endpoint. Must be between 0 and 65535.
+ /// An representing the specified IP address and port.
+ /// Thrown if is less than 0 or greater than 65535.
+ [UnsupportedOSPlatform("browser")]
+ public static IPEndPoint ConvertToIpEndPoint(string ipString, int port)
+ {
+ if (port < 0 || port > 65535)
+ {
+ throw new ArgumentOutOfRangeException(nameof(port), "Port must be between 0 and 65535.");
+ }
+
+ var address = ConvertToIPAddress(ipString);
+ return new IPEndPoint(address, port);
+ }
}
diff --git a/test/UnitTest/Services/TcpSocketFactoryTest.cs b/test/UnitTest/Services/TcpSocketFactoryTest.cs
index 8fe17e40001..67af9e4a901 100644
--- a/test/UnitTest/Services/TcpSocketFactoryTest.cs
+++ b/test/UnitTest/Services/TcpSocketFactoryTest.cs
@@ -274,9 +274,6 @@ public async Task FixLengthDataPackageHandler_Sticky()
tcs = new TaskCompletionSource();
await tcs.Task;
- // 等待第二次数据
- await tcs.Task;
-
// 验证第三次收到的数据
Assert.Equal(receivedBuffer.ToArray(), [3, 2, 3, 4, 5, 6, 7]);
diff --git a/test/UnitTest/Utils/UtilityTest.cs b/test/UnitTest/Utils/UtilityTest.cs
index ce578e3cc9a..839590248e5 100644
--- a/test/UnitTest/Utils/UtilityTest.cs
+++ b/test/UnitTest/Utils/UtilityTest.cs
@@ -10,6 +10,7 @@
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Globalization;
+using System.Net;
using System.Reflection;
using UnitTest.Components;
@@ -742,6 +743,23 @@ public void FormatIp_Test()
Assert.Equal("192.168.1.###", result);
}
+ [Fact]
+ public void ConvertToIPAddress_Ok()
+ {
+ var ex = Assert.Throws(() => Utility.ConvertToIPAddress(""));
+ Assert.NotNull(ex);
+
+ var address = Utility.ConvertToIPAddress("any");
+ Assert.Equal(IPAddress.Any, address);
+ }
+
+ [Fact]
+ public void ConvertToIpEndPoint_Ok()
+ {
+ var ex = Assert.Throws(() => Utility.ConvertToIpEndPoint("localhost", 88990));
+ Assert.NotNull(ex);
+ }
+
[AutoGenerateClass(Align = Alignment.Center)]
private class Dog
{