我们的安全部门报告说,我们的 WCF 服务没有实现超时或保持活动的方法,因此我正在调查我们拥有的配置并创建了非常简单的 WCF 服务,并注意到 ReceiveTimeout 看起来永远不会执行。
我怀疑,主要问题是我们已经实现了自己的保持活动解决方案,但是如果有人使用 Telnet 从 Windows cmd 连接,它不执行我们的 ping 消息,它应该在指定的 ReceiveTimeout 后关闭连接(根据我的理解) - 如果我错了,请纠正我 - 我在 WCF 方面经验不多)
我们使用 CoreWcf.NetTcp,因为应用程序最初是在 .net-framework 中创建的。
出于演示目的,我创建了此服务:
[ServiceContract]
public interface ITestWcfService
{
[OperationContract]
string GetData(int value);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class TestWcfService : ITestWcfService
{
public TestWcfService() //only instantiated by WCF
{
Console.WriteLine("Constructor");
OperationContext.Current.InstanceContext.Faulted += new EventHandler(Channel_Faulted);
OperationContext.Current.InstanceContext.Closed += new EventHandler(Channel_Faulted);
}
private void Channel_Faulted(object sender, EventArgs e)
{
Console.WriteLine("Faulted");
}
public string GetData(int value)
{
Console.WriteLine("GetData");
return $"{value}";
}
}
还有这堂课
public static class MyTestHostClass
{
public static string ServiceName => "WcfServiceName";
private static WebApplication _app;
public static void Open(int port)
{
var builder = WebApplication.CreateBuilder();
builder.WebHost.UseNetTcp(port);
builder.Services.AddServiceModelServices();
_app = builder.Build();
_app.UseServiceModel(serviceBuilder =>
{
serviceBuilder.AddService<TestWcfService>(options =>
{
});
serviceBuilder.AddServiceEndpoint<TestWcfService, ITestWcfService>(GetTcpBinding(), $"net.tcp://0.0.0.0:{port}/{ServiceName}");
});
_app.StartAsync().Wait();
}
public static void Close()
{
if (_app != null)
{
_app.StopAsync().Wait();
_app.DisposeAsync();
_app = null;
}
}
private static NetTcpBinding GetTcpBinding()
{
Console.WriteLine("Bindings");
var binding = new NetTcpBinding(SecurityMode.Transport)
{
CloseTimeout = TimeSpan.FromSeconds(10),
OpenTimeout = TimeSpan.FromSeconds(10),
ReceiveTimeout = new TimeSpan(0, 0, 0, 10),
SendTimeout = TimeSpan.FromSeconds(10)
};
return binding;
}
}
最后,我的控制台应用程序启动它:
Console.WriteLine("Hello, World!");
MyTestHostClass.Open(11999);
Console.WriteLine("WCF listening on port 11999");
await Task.Delay(Timeout.Infinite);
我可以看到消息
WCF is listening on port 11999
所以看起来一切顺利
然后我进入 Windows 中的 cmd 并运行 telnet 命令
telnet localhost 11999
- 它会连接但永远不会断开连接,即使它超过了 ReceiveTimeout。
我还在 WireShark 中检查了端口 11999 上没有任何活动,因此没有理由让它保持活动状态。
(我也没有看到来自 WCF 服务构造函数的控制台日志)
有什么想法需要设置以及为什么它不起作用?
编辑1:我尝试在简单的WinForms(net4.7.2)应用程序中托管相同的服务:
public Form1()
{
InitializeComponent();
host = new ServiceHost(typeof(TestWcfService));
var binding = new NetTcpBinding()
{
CloseTimeout = TimeSpan.FromSeconds(10),
OpenTimeout = TimeSpan.FromSeconds(10),
ReceiveTimeout = new TimeSpan(0, 0, 0, 10),
SendTimeout = TimeSpan.FromSeconds(10),
};
host.AddServiceEndpoint(typeof(ITestWcfService), binding, new Uri("net.tcp://localhost:11998/TestWcfService"));
host.Open();
Console.WriteLine("WCF Service listening on 11998");
}
并且它超时就好了。 这是否意味着 CoreWCF 包中存在错误,或者我缺少 CoreWCF 中需要完成的某些部分?
编辑2: 为了进行测试,我在framework4.7.2和NET6中创建了服务。然后是framework4.7.2和NET6中的客户端。 两项服务的服务合同相同。
客户端实现看起来像这样(实际上在框架和NET6中是相同的,唯一的区别是对于NET6我必须安装System.ServiceModel.NetTcp nuget。
private ITestWcfService Client;
public Form1()
{
InitializeComponent();
var myBinding = new NetTcpBinding()
{
CloseTimeout = TimeSpan.FromSeconds(5),
OpenTimeout = TimeSpan.FromSeconds(5),
ReceiveTimeout = new TimeSpan(0, 0, 0, 5),
SendTimeout = TimeSpan.FromSeconds(5)
};
var myEndpoint = new EndpointAddress("net.tcp://localhost:11998/TestWcfService");
var myChannelFactory = new ChannelFactory<ITestWcfService>(myBinding, myEndpoint);
try
{
Client = myChannelFactory.CreateChannel();
}
catch
{
}
}
private void button1_Click(object sender, EventArgs e)
{
Client.GetData(5);
}
以下是行为总结: 框架4.7.2行为:
NET6 行为:
但是,如果我尝试再次从客户端调用方法,它就无法到达。 5秒后超时(客户端超时)
所以根据我的理解,CoreWCF 包中存在一个错误。
如果有人发现这个问题并且遇到同样的问题,那么只需将 Nuget 包更新到新版本即可。此问题已在版本 1.5.2 及更高版本中修复。