WCF双工wsDualHttpBinding找不到引用contract的endpoint元素报错
WCF wsDualHttpBinding发布订阅模式客户端找不到endpoint报错
问题现象
基于WCF Duplex的wsDualHttpBinding实现发布-订阅模式的通知服务时,客户端执行订阅操作抛出如下错误:
在ServiceModel客户端配置节中找不到引用contract的endpoint元素。该问题可能是因为应用程序未找到对应配置文件,或是client节点下不存在匹配该contract的endpoint元素
现有配置与代码
客户端App.config配置
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <wsDualHttpBinding> <binding name="WSDualHttpBinding_IRecipeTemplateService" /> <binding name="Basic1" /> </wsDualHttpBinding> </bindings> <client> <endpoint address="http://172.18.26.14/CentralService/Services/NotificationService.svc" binding="wsDualHttpBinding" bindingConfiguration="Basic1" contract="CentralNotificationService.INotificationService" name="Basic1"> <identity> <servicePrincipalName value="host/myclienthost" /> </identity> </endpoint> </client> </system.serviceModel> </configuration>
服务端Web.config配置
<?xml version="1.0"?> <configuration> <configSections> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <appSettings> <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" /> </appSettings> <system.web> <compilation debug="true" targetFramework="4.6.1" /> <httpRuntime targetFramework="4.6.1"/> </system.web> <connectionStrings> <add name="RecipeDB" providerName="MySql.Data.MySqlClient" connectionString="Server=localhost; Port=3306; Database=recipe_db; Uid=root; Pwd=root" /> </connectionStrings> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework"/> <providers> <provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.EntityFramework"/> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer"/> </providers> </entityFramework> <system.data> <DbProviderFactories> <remove invariant="MySql.Data.MySqlClient" /> <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.0.28.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" /> </DbProviderFactories> </system.data> <system.serviceModel> <services> <service behaviorConfiguration="mexBehavior" name="RecipeServer.Central.Service.Services.NotificationService"> <endpoint address="" binding="wsDualHttpBinding" name="Basic" contract="RecipeServer.Central.Service.Interfaces.INotificationService" /> <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration="" name="mexdata" contract="IMetadataExchange" /> </service> </services> <behaviors> <serviceBehaviors> <behavior name="mexBehavior"> <serviceMetadata httpGetEnabled="true" /> </behavior> <behavior name=""> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> <protocolMapping> <add binding="wsDualHttpBinding" scheme="http" /> </protocolMapping> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel> <system.webServer> <modules runAllManagedModulesForAllRequests="true"/> <directoryBrowse enabled="true"/> </system.webServer> </configuration>
客户端实现代码
InstanceContext site = new InstanceContext(new ServiceCallBack()); try { client = new NotificationServiceClient(site); } catch (Exception e) { RecipeLogger.GetInstance.ErrorLog(e.Message); RecipeLogger.GetInstance.ErrorLog(e.StackTrace); } WSDualHttpBinding binding = (WSDualHttpBinding)client.Endpoint.Binding; RecipeLogger.GetInstance.DebugLog("2"); binding.ClientBaseAddress = new Uri("http://localhost:8000/myClient/"); string clientcallbackaddress = binding.ClientBaseAddress.AbsoluteUri; RecipeLogger.GetInstance.DebugLog(String.Format("Client baseadd {0}", clientcallbackaddress)); clientcallbackaddress += Guid.NewGuid().ToString(); binding.ClientBaseAddress = new Uri(clientcallbackaddress); RecipeLogger.GetInstance.DebugLog("Subscribe to central"); client.Subscribe(); client.Close();
问题根因
核心原因是客户端配置中endpoint的contract属性值与客户端代理类实际的契约完全限定名不匹配:
- 服务端定义的服务契约完全限定名为
RecipeServer.Central.Service.Interfaces.INotificationService - 客户端App.config中填写的contract为
CentralNotificationService.INotificationService,和代理类生成的契约命名空间不一致,WCF客户端构造时无法在配置节中找到匹配的endpoint节点,直接抛出配置错误。
另外现有代码存在空引用风险:如果NotificationServiceClient构造失败抛出异常,client对象为null,后续直接访问client.Endpoint.Binding会触发二次报错。
修复方案
- 修正客户端配置的contract值:找到项目中添加服务引用生成的
INotificationService接口的完整命名空间,将App.config中endpoint节点的contract属性替换为该完全限定名,保证名称、命名空间完全一致。如果没有配置域环境SPN,建议删除<identity>下的servicePrincipalName配置节点,避免额外身份验证错误。 - 跳过配置文件读取,手动构造客户端实例(推荐,避免配置不一致问题),修改后的客户端代码如下:
InstanceContext site = new InstanceContext(new ServiceCallBack()); // 手动构造双向绑定,配置回调地址 WSDualHttpBinding binding = new WSDualHttpBinding(); Uri callbackAddress = new Uri($"http://localhost:8000/myClient/{Guid.NewGuid()}"); binding.ClientBaseAddress = callbackAddress; // 指定服务端地址 EndpointAddress serviceAddress = new EndpointAddress("http://172.18.26.14/CentralService/Services/NotificationService.svc"); // 传入上下文、绑定、地址构造客户端,无需读取App.config配置 var client = new NotificationServiceClient(site, binding, serviceAddress); try { RecipeLogger.GetInstance.DebugLog("Subscribe to central"); client.Subscribe(); client.Close(); } catch (Exception ex) { RecipeLogger.GetInstance.ErrorLog(ex.Message); RecipeLogger.GetInstance.ErrorLog(ex.StackTrace); client.Abort(); }
- 额外排查项:确保本地8000端口未被防火墙拦截、未被其他程序占用,否则wsDualHttpBinding的回调通道无法建立,会出现订阅超时问题。
内容的提问来源于stack exchange,提问作者Le Danh
相关产品推荐
相关产品推荐

