@@ -69,6 +69,18 @@ public async Task ConnectAsync_Cancel()
6969 var cst = new CancellationTokenSource ( ) ;
7070 cst . Cancel ( ) ;
7171 var connect = await client . ConnectAsync ( "localhost" , 9999 , cst . Token ) ;
72+
73+ // 由于信号量被取消,所以连接会失败
74+ Assert . False ( connect ) ;
75+
76+ // 测试真正的连接被取消逻辑
77+ cst = new CancellationTokenSource ( ) ;
78+ _ = Task . Run ( async ( ) =>
79+ {
80+ await Task . Delay ( 50 ) ;
81+ cst . Cancel ( ) ;
82+ } ) ;
83+ connect = await client . ConnectAsync ( "localhost" , 9999 , cst . Token ) ;
7284 Assert . False ( connect ) ;
7385 }
7486
@@ -102,6 +114,29 @@ public async Task ConnectAsync_Error()
102114 methodInfo . Invoke ( client , [ LogLevel . Error , null ! , "Test error log" ] ) ;
103115 }
104116
117+ [ Fact ]
118+ public async Task ConnectAsync_Lock ( )
119+ {
120+ // 测试并发锁问题
121+ var provider = new MockAutoReconnectLockSocketProvider ( ) ;
122+ var client = CreateClient ( builder =>
123+ {
124+ builder . AddTransient < ISocketClientProvider > ( p => provider ) ;
125+ } ) ;
126+
127+ // 开 5 个线程同时连接
128+ _ = Task . Run ( async ( ) =>
129+ {
130+ // 延时 150 保证有一个连接失败
131+ await Task . Delay ( 150 ) ;
132+ provider . SetConnected ( true ) ;
133+ } ) ;
134+ var results = await Task . WhenAll ( Enumerable . Range ( 1 , 5 ) . Select ( i => client . ConnectAsync ( "localhost" , 0 ) . AsTask ( ) ) ) ;
135+ // 期望结果是 1个 false 4个 true
136+ Assert . Equal ( 1 , results . Count ( r => ! r ) ) ;
137+ Assert . Equal ( 4 , results . Count ( r => r ) ) ;
138+ }
139+
105140 [ Fact ]
106141 public async Task Send_Timeout ( )
107142 {
@@ -432,6 +467,7 @@ public async Task AutoReconnect_Cancel()
432467 } ) ;
433468
434469 await client . ConnectAsync ( "localhost" , 0 ) ;
470+ await Task . Delay ( 100 ) ;
435471 await client . DisposeAsync ( ) ;
436472 }
437473
@@ -912,6 +948,41 @@ public async ValueTask<bool> SendAsync(ReadOnlyMemory<byte> data, CancellationTo
912948 }
913949 }
914950
951+ class MockAutoReconnectLockSocketProvider : ISocketClientProvider
952+ {
953+ public bool IsConnected { get ; private set ; }
954+
955+ public IPEndPoint LocalEndPoint { get ; set ; } = new IPEndPoint ( IPAddress . Loopback , 0 ) ;
956+
957+ public async ValueTask < bool > ConnectAsync ( IPEndPoint endPoint , CancellationToken token = default )
958+ {
959+ await Task . Delay ( 100 , token ) ;
960+ return IsConnected ;
961+ }
962+
963+ public ValueTask < bool > SendAsync ( ReadOnlyMemory < byte > data , CancellationToken token = default )
964+ {
965+ return ValueTask . FromResult ( true ) ;
966+ }
967+
968+ public ValueTask < int > ReceiveAsync ( Memory < byte > buffer , CancellationToken token = default )
969+ {
970+ byte [ ] data = [ 1 , 2 , 3 , 4 , 5 ] ;
971+ data . CopyTo ( buffer ) ;
972+ return ValueTask . FromResult ( 5 ) ;
973+ }
974+
975+ public ValueTask CloseAsync ( )
976+ {
977+ return ValueTask . CompletedTask ;
978+ }
979+
980+ public void SetConnected ( bool state )
981+ {
982+ IsConnected = state ;
983+ }
984+ }
985+
915986 class MockAutoReconnectSocketProvider : ISocketClientProvider
916987 {
917988 public bool IsConnected { get ; private set ; }
0 commit comments