Skip to content

Conversation

@ArgoZhang
Copy link
Member

@ArgoZhang ArgoZhang commented Jun 20, 2025

Link issues

fixes #6265

Summary By Copilot

Regression?

  • Yes
  • No

Risk

  • High
  • Medium
  • Low

Verification

  • Manual (required)
  • Automated

Packaging changes reviewed?

  • Yes
  • No
  • N/A

☑️ Self Check before Merge

⚠️ Please check all items below before review. ⚠️

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • Merge the latest code from the main branch

Summary by Sourcery

Add ReceivedCallBack for raw data events on TCP socket client and integrate support in implementation, documentation, and tests.

New Features:

  • Introduce ReceivedCallBack property on ITcpSocketClient and DataPackageHandlerBase to handle incoming raw data asynchronously.
  • Enhance DefaultTcpSocketClient.ReceiveAsync to invoke ReceivedCallBack for each received data segment.

Documentation:

  • Update SocketFactories sample to document ReceivedCallBack usage and list built-in FixLengthDataPackageHandler and DelimiterDataPackageHandler.
  • Refine sample explanations with bold formatting on packet handling terms.

Tests:

  • Add unit test in TcpSocketFactoryTest to verify ReceivedCallBack invocation on data reception.

@bb-auto bb-auto bot added the enhancement New feature or request label Jun 20, 2025
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jun 20, 2025

Reviewer's Guide

Implements a ReceivedCallback on TCP clients and data handlers to allow consumers to process raw inbound bytes directly; updates default client, interface, handler base, samples, and tests to support and demonstrate this new callback.

Sequence diagram for data reception with ReceivedCallBack in DefaultTcpSocketClient

sequenceDiagram
    participant Client as DefaultTcpSocketClient
    participant Network as Network
    participant UserCallback as ReceivedCallBack
    participant Handler as DataPackageHandlerBase

    Network->>Client: Data arrives
    Client->>Client: ReceiveAsync()
    alt ReceivedCallBack is set
        Client->>UserCallback: Invoke ReceivedCallBack(buffer)
    end
    alt DataPackageHandler is set
        Client->>Handler: ReceiveAsync(buffer)
    end
Loading

Class diagram for ReceivedCallBack integration in TCP socket client

classDiagram
    class ITcpSocketClient {
        +IPEndPoint LocalEndPoint
        +Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack
        +SetDataHandler()
        +SendAsync()
        +Close()
    }
    class DefaultTcpSocketClient {
        +Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack
        +ReceiveAsync()
    }
    class DataPackageHandlerBase {
        +Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack
    }
    ITcpSocketClient <|.. DefaultTcpSocketClient
    IDataPackageHandler <|.. DataPackageHandlerBase
Loading

File-Level Changes

Change Details Files
Expose ReceivedCallback on ITcpSocketClient and implement invocation in DefaultTcpSocketClient
  • Add ReceivedCallBack property to interface ITcpSocketClient
  • Implement ReceivedCallBack property in DefaultTcpSocketClient
  • Invoke ReceivedCallBack within the receive loop before data handler processing
ITcpSocketClient.cs
DefaultTcpSocketClient.cs
Add ReceivedCallBack to DataPackageHandlerBase
  • Introduce ReceivedCallBack property with updated XML docs in base handler
DataPackageHandlerBase.cs
Update sample SocketFactories.razor to showcase ReceivedCallBack usage
  • Add bullet explaining ReceivedCallBack in usage list
  • Refine demo descriptions and formatting
  • Introduce new list of built-in data handlers section
SocketFactories.razor
Extend unit tests to cover ReceivedCallBack invocation
  • Register ReceivedCallBack in test using TaskCompletionSource
  • Connect and send sample data
  • Await callback and assert received bytes match sent data
TcpSocketFactoryTest.cs

Assessment against linked issues

Issue Objective Addressed Explanation
#6265 Add a ReceivedCallback property to the ITcpSocketClient interface.
#6265 Implement the ReceivedCallback property in the DefaultTcpSocketClient class.
#6265 Update documentation and samples to reflect the new ReceivedCallback property.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@bb-auto bb-auto bot added this to the 9.7.0 milestone Jun 20, 2025
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a 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:31` </location>
<code_context>
+    /// <remarks>The callback function should be designed to handle the received data efficiently and
+    /// asynchronously.  Ensure that the implementation does not block or perform long-running operations, as this may
+    /// impact performance.</remarks>
     public Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack { get; set; }

     /// <summary>
</code_context>

<issue_to_address>
Consider thread-safety for ReceivedCallBack property.

If this client may be used by multiple threads, ensure ReceivedCallBack is accessed in a thread-safe manner or clearly document that it must not be modified after connection.
</issue_to_address>

### Comment 2
<location> `src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs:141` </location>
<code_context>
                 {
                     buffer = buffer[..len];

+                    if (ReceivedCallBack != null)
+                    {
+                        await ReceivedCallBack(buffer);
+                    }
+
</code_context>

<issue_to_address>
Handle exceptions from ReceivedCallBack invocation.

Wrap the callback in a try-catch block to prevent unhandled exceptions from breaking the receive loop, and consider logging or handling errors appropriately.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
                    if (ReceivedCallBack != null)
                    {
                        await ReceivedCallBack(buffer);
                    }
=======
                    if (ReceivedCallBack != null)
                    {
                        try
                        {
                            await ReceivedCallBack(buffer);
                        }
                        catch (Exception ex)
                        {
                            Console.Error.WriteLine($"Exception in ReceivedCallBack: {ex}");
                            // Optionally, handle or rethrow as needed
                        }
                    }
>>>>>>> REPLACE

</suggested_fix>

### Comment 3
<location> `src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor:25` </location>
<code_context>
     <li>通过 <code>ITcpSocketClient</code> 实例方法 <code>SendAsync</code> 发送协议数据</li>
     <li>通过 <code>ITcpSocketClient</code> 实例方法 <code>Close</code> 关闭连接</li>
     <li>通过 <code>ITcpSocketClient</code> 实例方法 <code>SetDataHandler</code> 方法设置数据处理器</li>
+    <li>通过 <code>ITcpSocketClient</code> 实例属性 <code>ReceivedCallBack</code> 方法设置接收数据处理器(注意:此回调未做任何数据处理为原始数据)</li>
 </ul>

</code_context>

<issue_to_address>
Clarify the distinction between ReceivedCallBack and SetDataHandler in documentation.

Consider specifying when to use ReceivedCallBack versus SetDataHandler to help users choose the appropriate method.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
    <li>通过 <code>ITcpSocketClient</code> 实例方法 <code>SetDataHandler</code> 方法设置数据处理器</li>
    <li>通过 <code>ITcpSocketClient</code> 实例属性 <code>ReceivedCallBack</code> 方法设置接收数据处理器(注意:此回调未做任何数据处理为原始数据)</li>
</ul>
=======
    <li>通过 <code>ITcpSocketClient</code> 实例方法 <code>SetDataHandler</code> 方法设置数据处理器。<br />
        <b>适用场景:</b>推荐用于需要对接收到的数据进行协议解析、粘包/分包处理等高级数据处理的场景。数据会经过处理后再传递给回调函数,适合大多数业务开发需求。
    </li>
    <li>通过 <code>ITcpSocketClient</code> 实例属性 <code>ReceivedCallBack</code> 方法设置接收数据处理器(注意:此回调未做任何数据处理,为原始数据)。<br />
        <b>适用场景:</b>适合需要直接获取原始字节流数据、无需协议解析或自定义底层数据处理的场景。<br />
        <b>区别说明:</b><br />
        <ul>
            <li><code>ReceivedCallBack</code> 直接返回原始数据,适合底层自定义处理。</li>
            <li><code>SetDataHandler</code> 经过协议处理,适合业务层直接使用。</li>
        </ul>
        <b>建议:</b>如无特殊需求,优先使用 <code>SetDataHandler</code> 以简化开发流程。
    </li>
</ul>
>>>>>>> REPLACE

</suggested_fix>

### Comment 4
<location> `test/UnitTest/Services/TcpSocketFactoryTest.cs:164` </location>
<code_context>
         var data = new ReadOnlyMemory<byte>([1, 2, 3, 4, 5]);
         await client.SendAsync(data);

+        await tcs.Task;
+        Assert.Equal(buffer.ToArray(), [1, 2, 3, 4, 5]);
+
</code_context>

<issue_to_address>
Potential for test hang if callback is not invoked.

Add a timeout to the TaskCompletionSource await (e.g., Task.WhenAny with a timeout) to avoid indefinite hangs and improve test diagnostics.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
        await tcs.Task;
        Assert.Equal(buffer.ToArray(), [1, 2, 3, 4, 5]);
=======
        var completedTask = await Task.WhenAny(tcs.Task, Task.Delay(TimeSpan.FromSeconds(5)));
        if (completedTask != tcs.Task)
        {
            throw new TimeoutException("Timed out waiting for ReceivedCallBack to be invoked.");
        }
        Assert.Equal(buffer.ToArray(), [1, 2, 3, 4, 5]);
>>>>>>> REPLACE

</suggested_fix>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@codecov
Copy link

codecov bot commented Jun 20, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 100.00%. Comparing base (31a2a95) to head (8d36147).
Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #6266   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          711       711           
  Lines        31374     31379    +5     
  Branches      4431      4432    +1     
=========================================
+ Hits         31374     31379    +5     
Flag Coverage Δ
BB 100.00% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ArgoZhang ArgoZhang merged commit 4ce223e into main Jun 20, 2025
6 checks passed
@ArgoZhang ArgoZhang deleted the feat-socket branch June 20, 2025 04:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(ITcpSocketClient): add ReceivedCallback property

2 participants