Skip to content

Commit

Permalink
Merge pull request #58 from dotnet-campus/t/walterlv/hotfix
Browse files Browse the repository at this point in the history
Fix some unusual exceptions of the IPCs
  • Loading branch information
lindexi authored Jan 7, 2022
2 parents 4c388fe + 132d26e commit fa72230
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 10 deletions.
33 changes: 25 additions & 8 deletions src/dotnetCampus.Ipc/Internals/IpcPipeServerMessageProvider.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Threading.Tasks;

using dotnetCampus.Ipc.Context;
using dotnetCampus.Ipc.Pipes;
using dotnetCampus.Ipc.Utils.Extensions;

namespace dotnetCampus.Ipc.Internals
{
Expand Down Expand Up @@ -60,12 +62,18 @@ await Task.Factory.FromAsync(namedPipeServerStream.BeginWaitForConnection,
namedPipeServerStream.EndWaitForConnection, null).ConfigureAwait(false);
#endif
}
catch (IOException e)
catch (IOException ex)
{
// "管道已结束。"
// 当前服务关闭,此时异常符合预期
return;
}
catch (ObjectDisposedException ex)
{
// 当等待客户端连上此服务端期间,被调用了 Dispose 方法后,会抛出此异常。
// 日志在 Dispose 方法里记。
return;
}
//var streamMessageConverter = new StreamMessageConverter(namedPipeServerStream,
// IpcConfiguration.MessageHeader, IpcConfiguration.SharedArrayPool);
//streamMessageConverter.MessageReceived += OnClientConnectReceived;
Expand Down Expand Up @@ -107,16 +115,25 @@ private async Task SendAckAsync(Ack receivedAck)

public void Dispose()
{
if (ServerStreamMessageReader is null)
try
{
// 证明此时还没完全连接
NamedPipeServerStream.Dispose();
if (ServerStreamMessageReader is null)
{
// 证明此时还没完全连接
NamedPipeServerStream.Dispose();
}
else
{
// 证明已连接完成,此时不需要释放 NamedPipeServerStream 类
// 不在这一层释放 NamedPipeServerStream 类
ServerStreamMessageReader.Dispose();
}
}
else
finally
{
// 证明已连接完成,此时不需要释放 NamedPipeServerStream 类
// 不在这一层释放 NamedPipeServerStream 类
ServerStreamMessageReader.Dispose();
// 通过查看 Dispose 的堆栈来检查出异常时到底是谁在 Dispose。
IpcContext.Logger.Warning(@$"IpcPipeServerMessageProvider.Dispose
{new StackTrace()}");
}
}
}
Expand Down
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
4 changes: 4 additions & 0 deletions src/dotnetCampus.Ipc/Pipes/IpcClientService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
using System.Runtime.CompilerServices;
using System.Security.Principal;
Expand All @@ -9,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 @@ -83,12 +85,14 @@ public async Task Start(bool shouldRegisterToPeer = true)
var namedPipeClientStream = new NamedPipeClientStream(".", PeerName, PipeDirection.Out,
PipeOptions.None, TokenImpersonationLevel.Impersonation);
_namedPipeClientStreamTaskCompletionSource = new TaskCompletionSource<NamedPipeClientStream>();

#if NETCOREAPP
await namedPipeClientStream.ConnectAsync();
#else
// 在 NET45 没有 ConnectAsync 方法
await Task.Run(namedPipeClientStream.Connect);
#endif

if (!_namedPipeClientStreamTaskCompletionSource.Task.IsCompleted)
{
_namedPipeClientStreamTaskCompletionSource.SetResult(namedPipeClientStream);
Expand Down

0 comments on commit fa72230

Please sign in to comment.