我目前正在使用 gRPC,到目前为止已经创建了一个简单的 C# hello world 演示,使用 Visual Studio
ASP.Net Core gRPC Service
项目作为服务器,以及客户端控制台应用程序。
接下来我想做的是让 gRPC 服务器在 .Net 6 WPF 桌面应用程序(称为“AppA”)中运行,从而允许另一个桌面应用程序(“AppB”)向 AppA 发出信息请求。这可能吗?
通常我会使用 WCF 来实现此目的,但由于 CoreWCF 的不成熟,我被警告不要使用它。
我找到了一种在 WPF 桌面应用程序中托管 gRPC 服务的方法。
该项目需要引用“Grpc.AspNetCore”包(没有“proto”东西,稍后你会明白为什么)。
然后是启动 Web 应用程序的代码。出于演示应用程序的目的,我在 MainWindow.xaml.cs 中实现了此功能:
private WebApplication? _app;
public MainWindow()
{
InitializeComponent();
Task.Run(() => StartServer());
}
private void StartServer()
{
var builder = WebApplication.CreateBuilder();
// Despite adding a "launchSettings.json" file to the project, I couldn't find a way
// for the builder to pick it up, so had to configure the URLs here:
builder.WebHost.UseUrls("http://*:5219", "https://*:7219");
builder.Services.AddGrpc();
_app = builder.Build();
_app.MapGrpcService<TestService>();
_app.Run();
}
private void MainWindow_OnClosing(object? sender, CancelEventArgs e)
{
_app?.StopAsync();
}
对 _app.Run() 的调用会阻塞(直到 Web 应用程序停止),这就是为什么我在 b/g 线程上调用 StartServer() 的原因。
我发现一篇文章说proto编译器在WPF项目中不能正常工作。尽管它正在生成类,但构建错误表明无法找到这些类。 解决方案是将“protos”内容和服务类添加到类库中,然后从 WPF“服务器”引用它。类库项目需要引用以下包:
我们的 WPF .NET 6 应用程序是使用 Grpc.Core NUGET 包的 gRPC 服务器。我还没有迁移到新的 Grpc.NET 实现。当我这样做时,希望它不会强制使用 Asp.NET 而不是 WPF。以下是详细信息。希望它能带您到达您需要去的地方!
创建服务器定义后,以下是我们的 WPF 应用程序中创建服务器的方法:
private void CreateServer()
{
string[] ipAddresses = GetLocalIPAddresses();
// insecure
m_server = new Server
{
Services =
{
GrpcServer.BindService(this).Intercept(new IpAddressAuthenticator())
}
};
// Add the IP addresses
for (int i = 0; i < ipAddresses.Length; i++)
{
m_server.Ports.Add(new ServerPort(ipAddresses[i], remPort, ServerCredentials.Insecure));
}
}
这是 IpAddressAuthenticator 代码:
public class IpAddressAuthenticator : Interceptor
{
private readonly HashSet<string> m_authenticatedIps = new HashSet<string>()
{
"127.0.0.1",
"::1"
};
private void VerifyPeer(ServerCallContext context)
{
context.Status = new Status(StatusCode.OK, $"Authenticated peer: {context.Peer}");
}
private bool TryTakeIpAddress(string peer, out string ipAddress)
{
// ex.
// "ipv4:127.0.0.1:12345"
// "ipv6:[::1]:12345"
var ipv4Match = Regex.Match(peer, @"^ipv4:(.+):");
if (ipv4Match.Success)
{
ipAddress = ipv4Match.Groups[1].Value;
return true;
}
var ipv6Match = Regex.Match(peer, @"^ipv6:\[(.+)\]");
if (ipv6Match.Success)
{
ipAddress = ipv6Match.Groups[1].Value;
return true;
}
ipAddress = "";
return false;
}
public override Task<TResponse> ClientStreamingServerHandler<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, ServerCallContext context, ClientStreamingServerMethod<TRequest, TResponse> continuation)
{
VerifyPeer(context);
return base.ClientStreamingServerHandler(requestStream, context, continuation);
}
public override Task DuplexStreamingServerHandler<TRequest, TResponse>(IAsyncStreamReader<TRequest> requestStream, IServerStreamWriter<TResponse> responseStream, ServerCallContext context, DuplexStreamingServerMethod<TRequest, TResponse> continuation)
{
VerifyPeer(context);
return base.DuplexStreamingServerHandler(requestStream, responseStream, context, continuation);
}
public override Task ServerStreamingServerHandler<TRequest, TResponse>(TRequest request, IServerStreamWriter<TResponse> responseStream, ServerCallContext context, ServerStreamingServerMethod<TRequest, TResponse> continuation)
{
VerifyPeer(context);
return base.ServerStreamingServerHandler(request, responseStream, context, continuation);
}
public override Task<TResponse> UnaryServerHandler<TRequest, TResponse>(TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation)
{
VerifyPeer(context);
return base.UnaryServerHandler(request, context, continuation);
}
}