如何从自托管WCF服务进程获取运行端口(无需进程退出)?
问题与解决方案
问题描述
我有一个可自托管为WCF服务的程序,启动时通过特定参数启用该功能。之前采用传入指定端口的方式,现在需要改为自动查找可用端口并返回给调用方(SessionBound端点设置与当前问题无关)。服务自托管代码运行正常,最终将端口作为Main()方法的返回值。
客户端通过Process.Start启动该程序后,尝试读取ExitCode获取端口时触发InvalidOperationException(提示进程必须退出才能获取该信息),而使用WaitForExit则需要等待进程关闭,无法从运行中的程序获取端口,因此需要无需等待进程关闭即可获取返回值的方案。
可行实现方案
1. 标准输出传递端口
利用进程的标准输出流传递端口号,这是最简单直接的方式。
服务端修改
在获取到端口后,将端口号输出到标准输出并立即刷新:
int port = m_host.ChannelDispatchers.First().Listener.Uri.Port; // 输出端口到标准输出 Console.WriteLine(port); Console.Out.Flush(); // 后续SessionBound端点启动代码保持不变
客户端修改
配置进程启动信息以重定向标准输出,读取输出内容解析端口:
ProcessStartInfo psi = new ProcessStartInfo(exePath, "/remotingWCF") { UseShellExecute = false, RedirectStandardOutput = true // 开启标准输出重定向 }; mProcess = Process.Start(psi); // 读取服务端输出的端口号 string portStr = mProcess.StandardOutput.ReadLine(); if (int.TryParse(portStr, out int mPort)) { CreateWCFClient(mPort); }
2. 临时文件传递端口
通过约定路径的临时文件传递端口,适合对进程通信要求不高的场景。
服务端修改
获取端口后,将其写入带有进程ID的临时文件,避免多实例冲突:
int port = m_host.ChannelDispatchers.First().Listener.Uri.Port; string tempFilePath = Path.Combine(Path.GetTempPath(), $"aquator_port_{Process.GetCurrentProcess().Id}.txt"); File.WriteAllText(tempFilePath, port.ToString()); // 后续SessionBound端点启动代码保持不变
客户端修改
根据启动的进程ID构建临时文件路径,轮询读取文件内容:
ProcessStartInfo psi = new ProcessStartInfo(exePath, "/remotingWCF") { UseShellExecute = false }; mProcess = Process.Start(psi); string tempFilePath = Path.Combine(Path.GetTempPath(), $"aquator_port_{mProcess.Id}.txt"); int waitAttempts = 0; // 轮询等待文件生成,设置超时避免无限等待 while (!File.Exists(tempFilePath) && waitAttempts < 20) { Thread.Sleep(500); waitAttempts++; } if (File.Exists(tempFilePath)) { string portStr = File.ReadAllText(tempFilePath); if (int.TryParse(portStr, out int mPort)) { CreateWCFClient(mPort); // 读取完成后可删除临时文件 File.Delete(tempFilePath); } }
3. 命名管道进程通信
使用.NET命名管道实现可靠的进程间通信,适合需要双向通信或更高可靠性的场景。
服务端修改
创建命名管道服务器,等待客户端连接后发送端口号:
using System.IO.Pipes; int port = m_host.ChannelDispatchers.First().Listener.Uri.Port; // 管道名称包含进程ID确保唯一性 string pipeName = $"aquator_pipe_{Process.GetCurrentProcess().Id}"; using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(pipeName, PipeDirection.Out)) { // 等待客户端连接 pipeServer.WaitForConnection(); using (StreamWriter sw = new StreamWriter(pipeServer)) { sw.WriteLine(port.ToString()); sw.Flush(); } } // 后续SessionBound端点启动代码保持不变
客户端修改
连接对应命名管道,读取服务端发送的端口:
using System.IO.Pipes; ProcessStartInfo psi = new ProcessStartInfo(exePath, "/remotingWCF") { UseShellExecute = false }; mProcess = Process.Start(psi); string pipeName = $"aquator_pipe_{mProcess.Id}"; using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", pipeName, PipeDirection.In)) { // 设置连接超时时间 if (pipeClient.Connect(10000)) { using (StreamReader sr = new StreamReader(pipeClient)) { string portStr = sr.ReadLine(); if (int.TryParse(portStr, out int mPort)) { CreateWCFClient(mPort); } } } }
内容的提问来源于stack exchange,提问作者FunkyDexter
相关产品推荐
相关产品推荐

