-
-
Notifications
You must be signed in to change notification settings - Fork 363
feat(ITcpSocketClient): add Timeout parameter #6318
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Reviewer's GuideThis PR introduces configurable timeout parameters for connect, send, and receive operations in the TCP socket client; refactors the receive logic to support automatic and manual modes; updates data package handlers to propagate cancellation tokens; and expands unit tests to cover timeout and cancellation scenarios. Sequence diagram for TCP socket connect/send/receive with timeout and cancellationsequenceDiagram
participant Client as DefaultTcpSocketClient
participant Tcp as TcpClient
participant Handler as IDataPackageHandler
participant Logger
actor User
User->>Client: ConnectAsync(endPoint, token)
Client->>Tcp: new TcpClient(localEndPoint)
alt ConnectTimeout > 0
Client->>Tcp: ConnectAsync(endPoint, connectionToken with timeout)
else
Client->>Tcp: ConnectAsync(endPoint, token)
end
alt IsAutoReceive
Client->>Client: AutoReceiveAsync()
end
User->>Client: SendAsync(data, token)
alt SendTimeout > 0
Client->>Handler: SendAsync(data, sendToken with timeout)
Client->>Tcp: WriteAsync(data, sendToken with timeout)
else
Client->>Handler: SendAsync(data, token)
Client->>Tcp: WriteAsync(data, token)
end
User->>Client: ReceiveAsync(token)
alt ReceiveTimeout > 0
Client->>Tcp: ReadAsync(buffer, receiveToken with timeout)
Client->>Handler: ReceiveAsync(buffer, receiveToken with timeout)
else
Client->>Tcp: ReadAsync(buffer, token)
Client->>Handler: ReceiveAsync(buffer, token)
end
Note over Client,Logger: Logs warnings on cancellation or timeout
Class diagram for updated ITcpSocketClient interface and DefaultTcpSocketClient implementationclassDiagram
class ITcpSocketClient {
+int ReceiveBufferSize
+bool IsConnected
+bool IsAutoReceive
+int ConnectTimeout
+int SendTimeout
+int ReceiveTimeout
+IPEndPoint? LocalEndPoint
+Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack
+ValueTask<bool> ConnectAsync(IPEndPoint, CancellationToken)
+ValueTask<bool> SendAsync(ReadOnlyMemory<byte>, CancellationToken)
+ValueTask<Memory<byte>> ReceiveAsync(CancellationToken)
+void Close()
}
class DefaultTcpSocketClient {
-TcpClient? _client
-IDataPackageHandler? _dataPackageHandler
-IPEndPoint? _remoteEndPoint
-CancellationTokenSource? _receiveCancellationTokenSource
+IPEndPoint? LocalEndPoint
+ILogger<DefaultTcpSocketClient>? Logger
+int ReceiveBufferSize
+bool IsAutoReceive
+int ConnectTimeout
+int SendTimeout
+int ReceiveTimeout
+Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack
+ValueTask<bool> ConnectAsync(IPEndPoint, CancellationToken)
+ValueTask<bool> SendAsync(ReadOnlyMemory<byte>, CancellationToken)
+ValueTask<Memory<byte>> ReceiveAsync(CancellationToken)
+void SetDataHandler(IDataPackageHandler)
+void Close()
-ValueTask AutoReceiveAsync()
-ValueTask<int> ReceiveCoreAsync(TcpClient, Memory<byte>, CancellationToken)
}
ITcpSocketClient <|.. DefaultTcpSocketClient
Class diagram for updated IDataPackageHandler and DataPackageHandlerBaseclassDiagram
class IDataPackageHandler {
+Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack
+ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte>, CancellationToken)
+ValueTask ReceiveAsync(ReadOnlyMemory<byte>, CancellationToken)
}
class DataPackageHandlerBase {
+Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack
+virtual ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte>, CancellationToken)
+virtual ValueTask ReceiveAsync(ReadOnlyMemory<byte>, CancellationToken)
}
IDataPackageHandler <|.. DataPackageHandlerBase
class DelimiterDataPackageHandler {
+override ValueTask ReceiveAsync(ReadOnlyMemory<byte>, CancellationToken)
}
DataPackageHandlerBase <|-- DelimiterDataPackageHandler
class FixLengthDataPackageHandler {
+override ValueTask ReceiveAsync(ReadOnlyMemory<byte>, CancellationToken)
}
DataPackageHandlerBase <|-- FixLengthDataPackageHandler
Class diagram for updated method signatures in DataPackageHandlerBase and derived classesclassDiagram
class DataPackageHandlerBase {
+virtual ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte>, CancellationToken)
+virtual ValueTask ReceiveAsync(ReadOnlyMemory<byte>, CancellationToken)
}
class DelimiterDataPackageHandler {
+override ValueTask ReceiveAsync(ReadOnlyMemory<byte>, CancellationToken)
}
class FixLengthDataPackageHandler {
+override ValueTask ReceiveAsync(ReadOnlyMemory<byte>, CancellationToken)
}
DataPackageHandlerBase <|-- DelimiterDataPackageHandler
DataPackageHandlerBase <|-- FixLengthDataPackageHandler
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @ArgoZhang - I've reviewed your changes and they look great!
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs:35` </location>
<code_context>
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)
</code_context>
<issue_to_address>
Timeout properties are set as integers without validation.
Please add validation to ensure these timeout properties cannot be set to negative values, as this could cause errors.
</issue_to_address>
### Comment 2
<location> `src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs:58` </location>
<code_context>
- // 开始接收数据
- _ = Task.Run(ReceiveAsync, token);
+ var connectionToken = token;
+ if (ConnectTimeout > 0)
+ {
+ // 设置连接超时时间
+ var connectTokenSource = new CancellationTokenSource(ConnectTimeout);
+ connectionToken = CancellationTokenSource.CreateLinkedTokenSource(token, connectTokenSource.Token).Token;
+ }
+ await _client.ConnectAsync(endPoint, connectionToken);
</code_context>
<issue_to_address>
CancellationTokenSource for timeouts is not disposed.
Not disposing of the CancellationTokenSource may cause resource leaks. Use a using statement or explicitly dispose of it after use.
</issue_to_address>
### Comment 3
<location> `src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs:171` </location>
<code_context>
- var buffer = block.Memory;
- var stream = _client.GetStream();
- var len = await stream.ReadAsync(buffer, _receiveCancellationTokenSource.Token);
- if (len == 0)
+ // 设置接收超时时间
+ var receiveTokenSource = new CancellationTokenSource(ReceiveTimeout);
+ receiveToken = CancellationTokenSource.CreateLinkedTokenSource(receiveToken, receiveTokenSource.Token).Token;
+ }
+ len = await stream.ReadAsync(buffer, receiveToken);
+ if (len == 0)
+ {
+ // 远端主机关闭链路
+ Logger.LogInformation("TCP Socket {LocalEndPoint} received 0 data closed by {RemoteEndPoint}", LocalEndPoint, _remoteEndPoint);
+ }
+ else
</code_context>
<issue_to_address>
Zero-length read is logged as information, but connection is not closed.
Consider adding logic to close or clean up the connection when a zero-length read occurs to prevent further operations on a closed socket.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
if (len == 0)
{
// 远端主机关闭链路
Logger.LogInformation("TCP Socket {LocalEndPoint} received 0 data closed by {RemoteEndPoint}", LocalEndPoint, _remoteEndPoint);
}
else
=======
if (len == 0)
{
// 远端主机关闭链路
Logger.LogInformation("TCP Socket {LocalEndPoint} received 0 data closed by {RemoteEndPoint}", LocalEndPoint, _remoteEndPoint);
// 关闭连接并清理资源
try
{
_client?.Close();
}
catch (Exception closeEx)
{
Logger.LogWarning(closeEx, "Exception occurred while closing TCP Socket {LocalEndPoint}", LocalEndPoint);
}
break; // 退出循环,防止后续操作
}
else
>>>>>>> REPLACE
</suggested_fix>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Link issues
fixes #6317
Summary By Copilot
Regression?
Risk
Verification
Packaging changes reviewed?
☑️ Self Check before Merge
Summary by Sourcery
Add timeout parameters and manual receive control to the TCP socket client interface and implementation, integrate consistent cancellation and logging, update data package handlers for cancelable operations, and expand unit tests to cover the new features and edge cases
New Features:
Enhancements:
Documentation:
Tests: