Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 9 additions & 36 deletions src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,44 +12,30 @@
namespace BootstrapBlazor.Components;

[UnsupportedOSPlatform("browser")]
sealed class DefaultTcpSocketClient(IPEndPoint localEndPoint) : ITcpSocketClient
sealed class DefaultTcpSocketClient(IPEndPoint localEndPoint) : TcpSocketClientBase
{
private TcpClient? _client;
private IDataPackageHandler? _dataPackageHandler;
private CancellationTokenSource? _receiveCancellationTokenSource;
private IPEndPoint? _remoteEndPoint;

public bool IsConnected => _client?.Connected ?? false;

public IPEndPoint? LocalEndPoint { get; set; }
public override bool IsConnected => _client?.Connected ?? false;

[NotNull]
public ILogger<DefaultTcpSocketClient>? Logger { get; set; }

public int ReceiveBufferSize { get; set; } = 1024 * 64;

public bool IsAutoReceive { get; set; } = true;

public Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack { get; set; }

public int ConnectTimeout { get; set; }

public int SendTimeout { get; set; }

public int ReceiveTimeout { get; set; }

public void SetDataHandler(IDataPackageHandler handler)
public override void SetDataHandler(IDataPackageHandler handler)
{
_dataPackageHandler = handler;
}

public async ValueTask<bool> ConnectAsync(IPEndPoint endPoint, CancellationToken token = default)
public override async ValueTask<bool> ConnectAsync(IPEndPoint endPoint, CancellationToken token = default)
{
var ret = false;
try
{
// 释放资源
Close();
await Close();

// 创建新的 TcpClient 实例
_client ??= new TcpClient(localEndPoint);
Expand Down Expand Up @@ -91,7 +77,7 @@ public async ValueTask<bool> ConnectAsync(IPEndPoint endPoint, CancellationToken
return ret;
}

public async ValueTask<bool> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
public override async ValueTask<bool> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
{
if (_client is not { Connected: true })
{
Expand Down Expand Up @@ -137,7 +123,7 @@ public async ValueTask<bool> SendAsync(ReadOnlyMemory<byte> data, CancellationTo
return ret;
}

public async ValueTask<Memory<byte>> ReceiveAsync(CancellationToken token = default)
public override async ValueTask<Memory<byte>> ReceiveAsync(CancellationToken token = default)
{
if (_client == null || !_client.Connected)
{
Expand Down Expand Up @@ -228,12 +214,7 @@ private async ValueTask<int> ReceiveCoreAsync(TcpClient client, Memory<byte> buf
return len;
}

public void Close()
{
Dispose(true);
}

private void Dispose(bool disposing)
protected override ValueTask DisposeAsync(bool disposing)
{
if (disposing)
{
Expand All @@ -255,14 +236,6 @@ private void Dispose(bool disposing)
_client = null;
}
}
}

/// <summary>
/// <inheritdoc/>
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
return ValueTask.CompletedTask;
}
}
4 changes: 2 additions & 2 deletions src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace BootstrapBlazor.Components;
/// <summary>
/// Represents a TCP socket for network communication.
/// </summary>
public interface ITcpSocketClient : IDisposable
public interface ITcpSocketClient : IAsyncDisposable
{
/// <summary>
/// Gets or sets the size, in bytes, of the receive buffer used for network operations.
Expand Down Expand Up @@ -110,5 +110,5 @@ public interface ITcpSocketClient : IDisposable
/// <remarks>Once the connection or resource is closed, it cannot be reopened. Ensure that all necessary
/// operations are completed before calling this method. This method is typically used to clean up resources when
/// they are no longer needed.</remarks>
void Close();
ValueTask Close();
}
123 changes: 123 additions & 0 deletions src/BootstrapBlazor/Services/TcpSocket/TcpSocketClientBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone

using System.Net;

namespace BootstrapBlazor.Components;

/// <summary>
/// Provides a base implementation for a TCP socket client, enabling connection, data transmission, and reception over
/// TCP.
/// </summary>
/// <remarks>This abstract class serves as a foundation for implementing TCP socket clients. It provides methods
/// for connecting to a remote endpoint, sending and receiving data, and managing connection state. Derived classes can
/// extend or customize the behavior as needed.</remarks>
public abstract class TcpSocketClientBase : ITcpSocketClient
{
/// <summary>
/// <inheritdoc/>
/// </summary>
public abstract bool IsConnected { get; }

/// <summary>
/// <inheritdoc/>
/// </summary>
public IPEndPoint? LocalEndPoint { get; set; }

/// <summary>
/// <inheritdoc/>
/// </summary>
public int ReceiveBufferSize { get; set; } = 1024 * 64;

/// <summary>
/// <inheritdoc/>
/// </summary>
public bool IsAutoReceive { get; set; } = true;

/// <summary>
/// <inheritdoc/>
/// </summary>
public Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack { get; set; }

/// <summary>
/// <inheritdoc/>
/// </summary>
public int ConnectTimeout { get; set; }

/// <summary>
/// <inheritdoc/>
/// </summary>
public int SendTimeout { get; set; }

/// <summary>
/// <inheritdoc/>
/// </summary>
public int ReceiveTimeout { get; set; }

/// <summary>
/// <inheritdoc/>
/// </summary>
public virtual void SetDataHandler(IDataPackageHandler handler)
{

}

/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="endPoint"></param>
/// <param name="token"></param>
/// <returns></returns>
public abstract ValueTask<bool> ConnectAsync(IPEndPoint endPoint, CancellationToken token = default);

/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="data"></param>
/// <param name="token"></param>
/// <returns></returns>
public abstract ValueTask<bool> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default);

/// <summary>
/// <inheritdoc/>
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public abstract ValueTask<Memory<byte>> ReceiveAsync(CancellationToken token = default);

/// <summary>
/// <inheritdoc/>
/// </summary>
public virtual ValueTask Close()
{
return DisposeAsync(true);
}

/// <summary>
/// Releases the resources used by the current instance of the class.
/// </summary>
/// <remarks>This method is called to free both managed and unmanaged resources. If the <paramref
/// name="disposing"/> parameter is <see langword="true"/>, the method releases managed resources in addition to
/// unmanaged resources. Override this method in a derived class to provide custom cleanup logic.</remarks>
/// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> to release only
/// unmanaged resources.</param>
protected virtual ValueTask DisposeAsync(bool disposing)
{
if (disposing)
{
LocalEndPoint = null;
}
return ValueTask.CompletedTask;
}

/// <summary>
/// <inheritdoc/>
/// </summary>
public async ValueTask DisposeAsync()
{
await DisposeAsync(true);
GC.SuppressFinalize(this);
}
}
Loading