Conversation
添加 TouchSocket 包引用,删除 DefaultTcpSocketClient 类,使用 TouchSocketTcpClient 类实现 ITcpSocketClient 接口,提供连接、发送和接收数据的功能。修改 DefaultTcpSocketFactory 类构造函数,移除对 ILogger<DefaultTcpSocketClient> 的依赖,改为使用 TouchSocketTcpClient。新增 TouchSocketTcpClient 类实现 TCP 连接逻辑。
# Conflicts: # BootstrapBlazor.Extensions.sln
在 DefaultTcpSocketClient 类中,修改 SendAsync 方法的调用方式,去掉 WaitAsync,直接使用 SendAsync(data, sendToken),使代码更简洁并提高异步操作效率
在 `BootstrapBlazor.TouchSocket.csproj` 文件中,将 `TouchSocket` 的版本从 `3.1.11.8` 更新为 `4.0.0-Alpha.10`。 在 `DefaultTcpSocketProvider.cs` 文件中,类的访问修饰符从 `sealed` 改为 `internal`,并添加了对 `System.Diagnostics` 的引用。 修改 `IsConnected` 属性的实现,确保调用基类的 `Online` 属性。 添加新的 `CloseAsync` 方法,重写连接逻辑以包含异常处理,并在连接成功后进行调试断言。 更新 `ReceiveAsync` 方法的数据读取逻辑,使用基类的 `Transport.Input` 进行数据读取,并确保正确处理数据缓冲区。 重写 `SendAsync` 方法,添加对取消令牌的处理,并确保在发送数据时正确管理锁。 重写 `ReceiveLoopAsync` 方法,注释说明新的接收循环逻辑,确保数据通过管道直接读取,而不进行额外的数据读取操作
# Conflicts: # BootstrapBlazor.Extensions.sln
Reviewer's GuideThe PR introduces a new TcpSocket extension library based on TouchSocket with a custom provider, DI registration, comprehensive unit tests, and project file updates; it also adds a new OfficeDocumentViewer component with Razor, JS interop, and styling. Class diagram for DefaultTcpSocketProvider and ISocketClientProviderclassDiagram
class ISocketClientProvider {
<<interface>>
+bool IsConnected
+IPEndPoint LocalEndPoint
+ValueTask CloseAsync()
+ValueTask<bool> ConnectAsync(IPEndPoint, CancellationToken)
+ValueTask<int> ReceiveAsync(Memory<byte>, CancellationToken)
+ValueTask<bool> SendAsync(ReadOnlyMemory<byte>, CancellationToken)
}
class TcpClientBase {
+bool Online
+Task CloseAsync(string)
+Task TcpConnectAsync(int, CancellationToken)
+Transport Transport
+Logger Logger
+void ThrowIfTcpClientNotConnected()
+void ThrowIfDisposed()
}
class DefaultTcpSocketProvider {
+bool IsConnected
+IPEndPoint LocalEndPoint
+ValueTask CloseAsync()
+ValueTask<bool> ConnectAsync(IPEndPoint, CancellationToken)
+ValueTask<int> ReceiveAsync(Memory<byte>, CancellationToken)
+ValueTask<bool> SendAsync(ReadOnlyMemory<byte>, CancellationToken)
-Task ReceiveLoopAsync(ITransport)
}
ISocketClientProvider <|.. DefaultTcpSocketProvider
TcpClientBase <|-- DefaultTcpSocketProvider
Class diagram for OfficeDocumentViewer componentclassDiagram
class OfficeDocumentViewer {
+string? Url
+string? Height
+Func<Task>? OnLoaded
-NavigationManager NavigationManager
-string? ClassString
-string? StyleString
-string? _url
+Task OnAfterRenderAsync(bool)
+Task InvokeInitAsync()
-string GetAbsoluteUri(string?)
+Task TriggerOnLoaded()
}
class BootstrapModuleComponentBase {
}
OfficeDocumentViewer --|> BootstrapModuleComponentBase
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.
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/components/BootstrapBlazor.OfficeDocumentViewer/OfficeDocumentViewer.razor.js:23` </location>
<code_context>
+ if (url) {
+ const { frame } = officeViewer;
+ const viewer = frame || createFrame(el);
+ if (options.loadedCallaback) {
+ viewer.onload = () => {
+ invoke.invokeMethodAsync(options.loadedCallaback);
</code_context>
<issue_to_address>
Typo in property access: 'loadedCallaback' should be 'loadedCallback'.
This typo could cause runtime errors due to referencing a non-existent property.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
if (options.loadedCallaback) {
viewer.onload = () => {
invoke.invokeMethodAsync(options.loadedCallaback);
=======
if (options.loadedCallback) {
viewer.onload = () => {
invoke.invokeMethodAsync(options.loadedCallback);
>>>>>>> REPLACE
</suggested_fix>
### Comment 2
<location> `src/extensions/BootstrapBlazor.TouchSocket/Services/DefaultTcpSocketProvider.cs:14` </location>
<code_context>
+
+namespace BootstrapBlazor.Components;
+
+internal sealed class DefaultTcpSocketProvider : TcpClientBase, ISocketClientProvider
+{
+ /// <summary>
</code_context>
<issue_to_address>
Consider replacing the custom pipeline and semaphore logic with a simple NetworkStream to handle reading and writing.
```suggestion
The manual PipeReader/PipeWriter plumbing, custom ReceiveLoopAsync override, and semaphore handling can be replaced by a simple NetworkStream over `MainSocket`. This preserves all behavior while dramatically cutting complexity.
1. Add a private `NetworkStream` field:
```csharp
private NetworkStream? _networkStream;
```
2. Initialize it in `ConnectAsync` (right after `TcpConnectAsync`):
```csharp
await TcpConnectAsync(int.MaxValue, token);
_networkStream = new NetworkStream(MainSocket!, ownsSocket: true);
```
3. Simplify `ReceiveAsync` to use `NetworkStream.ReadAsync`:
```csharp
public async ValueTask<int> ReceiveAsync(Memory<byte> buffer, CancellationToken token = default)
{
token.ThrowIfCancellationRequested();
ThrowIfTcpClientNotConnected(); // unchanged
ThrowIfDisposed(); // unchanged
try
{
int bytesRead = await _networkStream!.ReadAsync(buffer, token);
return bytesRead;
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
Logger?.Exception(this, ex);
return 0;
}
}
```
4. Simplify `SendAsync` to use `NetworkStream.WriteAsync` (remove semaphore):
```csharp
public async ValueTask<bool> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
{
token.ThrowIfCancellationRequested();
ThrowIfTcpClientNotConnected();
ThrowIfDisposed();
try
{
await _networkStream!.WriteAsync(data, token);
await _networkStream.FlushAsync(token);
return true;
}
catch (Exception ex)
{
Logger?.Exception(this, ex);
return false;
}
}
```
5. Remove the entire `ReceiveLoopAsync` override—it's no longer needed since reads happen on demand.
These changes maintain the same public API and semantics but drop most of the custom pipeline plumbing.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| if (options.loadedCallaback) { | ||
| viewer.onload = () => { | ||
| invoke.invokeMethodAsync(options.loadedCallaback); |
There was a problem hiding this comment.
issue (typo): Typo in property access: 'loadedCallaback' should be 'loadedCallback'.
This typo could cause runtime errors due to referencing a non-existent property.
| if (options.loadedCallaback) { | |
| viewer.onload = () => { | |
| invoke.invokeMethodAsync(options.loadedCallaback); | |
| if (options.loadedCallback) { | |
| viewer.onload = () => { | |
| invoke.invokeMethodAsync(options.loadedCallback); |
|
|
||
| namespace BootstrapBlazor.Components; | ||
|
|
||
| internal sealed class DefaultTcpSocketProvider : TcpClientBase, ISocketClientProvider |
There was a problem hiding this comment.
issue (complexity): Consider replacing the custom pipeline and semaphore logic with a simple NetworkStream to handle reading and writing.
| internal sealed class DefaultTcpSocketProvider : TcpClientBase, ISocketClientProvider | |
| The manual PipeReader/PipeWriter plumbing, custom ReceiveLoopAsync override, and semaphore handling can be replaced by a simple NetworkStream over `MainSocket`. This preserves all behavior while dramatically cutting complexity. | |
| 1. Add a private `NetworkStream` field: | |
| ```csharp | |
| private NetworkStream? _networkStream; |
- Initialize it in
ConnectAsync(right afterTcpConnectAsync):
await TcpConnectAsync(int.MaxValue, token);
_networkStream = new NetworkStream(MainSocket!, ownsSocket: true);- Simplify
ReceiveAsyncto useNetworkStream.ReadAsync:
public async ValueTask<int> ReceiveAsync(Memory<byte> buffer, CancellationToken token = default)
{
token.ThrowIfCancellationRequested();
ThrowIfTcpClientNotConnected(); // unchanged
ThrowIfDisposed(); // unchanged
try
{
int bytesRead = await _networkStream!.ReadAsync(buffer, token);
return bytesRead;
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
Logger?.Exception(this, ex);
return 0;
}
}- Simplify
SendAsyncto useNetworkStream.WriteAsync(remove semaphore):
public async ValueTask<bool> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
{
token.ThrowIfCancellationRequested();
ThrowIfTcpClientNotConnected();
ThrowIfDisposed();
try
{
await _networkStream!.WriteAsync(data, token);
await _networkStream.FlushAsync(token);
return true;
}
catch (Exception ex)
{
Logger?.Exception(this, ex);
return false;
}
}- Remove the entire
ReceiveLoopAsyncoverride—it's no longer needed since reads happen on demand.
These changes maintain the same public API and semantics but drop most of the custom pipeline plumbing.
Link issues
fixes #511
Summary By Copilot
Regression?
Risk
Verification
Packaging changes reviewed?
☑️ Self Check before Merge
Summary by Sourcery
Add a new TCP socket extension and an Office document viewer component
New Features:
Enhancements:
Build: