Skip to content

Commit

Permalink
解决重连时出现的另一 FileNotFoundException
Browse files Browse the repository at this point in the history
  • Loading branch information
walterlv committed Jan 7, 2022
1 parent 9b16101 commit 132d26e
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 22 deletions.
38 changes: 36 additions & 2 deletions src/dotnetCampus.Ipc/Internals/PeerReConnector.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using dotnetCampus.Ipc.Context;
using System.IO;
using System.Threading.Tasks;

using dotnetCampus.Ipc.Context;
using dotnetCampus.Ipc.Pipes;

namespace dotnetCampus.Ipc.Internals
Expand Down Expand Up @@ -27,7 +30,38 @@ private void PeerProxy_PeerConnectionBroken(object? sender, IPeerConnectionBroke
private async void Reconnect()
{
var ipcClientService = _ipcProvider.CreateIpcClientService(_peerProxy.PeerName);
await ipcClientService.Start();
for (int i = 0; i < 4; i++)
{
try
{
await ipcClientService.Start();
break;
}
catch (FileNotFoundException ex)
{
// ## 此异常有两种
//
// 1. 一种来自于 namedPipeClientStream.ConnectAsync(),刚调用时还能获取到管道句柄,但马上与之连接时便已断开。
// 2. 另一种来自 RegisterToPeer(),前面已经连上了,但试图发消息时便已断开。
//
// ## 然而,为什么一连上就断开了呢?
//
// 这是因为每个端有两条管道,各自作为服务端和客户端。
// 当重连时,靠的是服务端管道读到末尾来判断的;但此时重连的却是客户端。
// 有极少情况下,这两条的断开时间间隔足够长到本方法的客户端已开始重连。
// 那么,本方法的客户端在一开始试图连接对方时连上了,但随即就完成了之前没完成的断开,于是出现 FileNotFoundException。
//
// ## 那么,如何解决呢?
//
// 通过重连,我们可以缓解因对方正在断开导致的我们误连。通过重连多次,可以更大概率缓解以至于解决此异常。
//
// ## 是否有后续问题?
//
// 有可能本方法已全部完成之后才断开吗?不可能,因为 RegisterToPeer() 会发消息的,如果是对方进程退出等原因导致的断连,那么消息根本就无法发送。
// 因为本调用内会置一个 TaskCompleteSource,所以也会导致一起等待此任务的其他发送全部失败,而解决方法就是在其他发送处也重试。
await Task.Delay(16);
}
}
_peerProxy.Reconnect(ipcClientService);
}
}
Expand Down
24 changes: 4 additions & 20 deletions src/dotnetCampus.Ipc/Pipes/IpcClientService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using dotnetCampus.Ipc.Diagnostics;
using dotnetCampus.Ipc.Internals;
using dotnetCampus.Ipc.Messages;
using dotnetCampus.Ipc.Utils;
using dotnetCampus.Ipc.Utils.Extensions;
using dotnetCampus.Ipc.Utils.Logging;
using dotnetCampus.Threading;
Expand Down Expand Up @@ -85,29 +86,12 @@ public async Task Start(bool shouldRegisterToPeer = true)
PipeOptions.None, TokenImpersonationLevel.Impersonation);
_namedPipeClientStreamTaskCompletionSource = new TaskCompletionSource<NamedPipeClientStream>();

// 带有重试的连接。
for (int i = 0; i < 4; i++)
{
try
{
#if NETCOREAPP
await namedPipeClientStream.ConnectAsync();
await namedPipeClientStream.ConnectAsync();
#else
// 在 NET45 没有 ConnectAsync 方法
await Task.Run(namedPipeClientStream.Connect);
// 在 NET45 没有 ConnectAsync 方法
await Task.Run(namedPipeClientStream.Connect);
#endif
break;
}
catch (FileNotFoundException ex)
{
// 因为每个端有两条管道,各自作为服务端和客户端。
// 当重连时,靠的是服务端管道读到末尾来判断的;但此时重连的却是客户端。
// 有极少情况下,这两条的断开时间间隔足够长到本方法的客户端已开始重连。
// 那么,本方法的客户端在一开始试图连接对方时连上了,但随即就完成了之前没完成的断开,于是出现 FileNotFoundException。
// 这时,我们通过重新连接一次可以再次等对方重新启动完再连。
await Task.Delay(16);
}
}

if (!_namedPipeClientStreamTaskCompletionSource.Task.IsCompleted)
{
Expand Down

0 comments on commit 132d26e

Please sign in to comment.