【问题标题】:Connecting to WCF running as a Windows Service on another computer连接到在另一台计算机上作为 Windows 服务运行的 WCF
【发布时间】:2020-05-14 11:33:21
【问题描述】:

我有一个 WinForms 客户端应用程序。作为我编写的 Windows 服务的一部分,我在另一台计算机上运行 WCF 服务器。我首先尝试了NetPipeBinding,现在尝试了NetTcpBinding。我确实在 具有高级安全性的 Windows 防火墙 中打开了 2 个端口。服务器操作系统是 Windows Server 2016

这是服务器代码:

public Boolean CreatePipeServer(out string logEntry)
{
    try
    {
        NetTcpBinding ipcBinding = new NetTcpBinding()
        {
            Security = new NetTcpSecurity()
            {
                Mode = SecurityMode.None,
                Transport = new TcpTransportSecurity()
                {
                    ClientCredentialType = TcpClientCredentialType.None,
                    ProtectionLevel = System.Net.Security.ProtectionLevel.None,
                },
            },
            MaxBufferPoolSize = 6553600,
            MaxBufferSize = 6553600,
            MaxReceivedMessageSize = 6553600,
            MaxConnections = 1000,
            ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
            {
                MaxArrayLength = 2147483647,
                MaxBytesPerRead = 2147483647,
                MaxDepth = 2147483647,
                MaxNameTableCharCount = 2147483647,
                MaxStringContentLength = 2147483647,
            },
        };

        // Instantiate: Host Service
        Uri uriMex = new Uri("http://localhost:8000");
        this.ServiceParent.ServiceIpcAppToService.HostIpcAppToService = new ServiceHost(typeof(IpcAppToService), uriMex);

        // Instantiate: Endpoint
        Uri uriService = new Uri("net.tcp://localhost:8004/StampIpcAppToService");
        this.ServiceParent.ServiceIpcAppToService.HostIpcAppToService.AddServiceEndpoint(typeof(StampIpc.IIpcAppToService), ipcBinding, uriService);

        // Instantiate: Service Meta Behavior
        ServiceMetadataBehavior ipcSmb = this.ServiceParent.ServiceIpcAppToService.HostIpcAppToService.Description.Behaviors.Find<ServiceMetadataBehavior>();
        if (null == ipcSmb)
        {
            ipcSmb = new ServiceMetadataBehavior()
            {
                HttpGetEnabled = true,
            };
            this.ServiceParent.ServiceIpcAppToService.HostIpcAppToService.Description.Behaviors.Add(ipcSmb);
            ServiceDescription serviceDescription = this.ServiceParent.ServiceIpcAppToService.HostIpcAppToService.Description;
        }

        // Instantiate: MEX Biding
        Binding ipcMexBinding = MetadataExchangeBindings.CreateMexHttpBinding();
        this.ServiceParent.ServiceIpcAppToService.HostIpcAppToService.AddServiceEndpoint(typeof(IMetadataExchange), ipcMexBinding, "MEX");

        // Capture events.
        ...

        // Start the service.
        this.ServiceParent.ServiceIpcAppToService.HostIpcAppToService.Open();

        return true;
    }

    catch (Exception) { }

return false;
}

我正在尝试通过转到Visual Studio 2019 中的Add &gt; Service Reference... 来测试有效的通信链接。无论我输入什么,我都会不断收到一种或另一种错误。目前,我放置:

net.tcp://10.10.70.134:8004/StampIpcAppToService

这会引发错误:

URI 前缀无法识别。 元数据包含无法解析的引用:“net.tcp://10.10.70.134:8004/StampIpcAppToService”。 元数据包含无法解析的引用:“net.tcp://10.10.70.134:8004/StampIpcAppToService”。 如果在当前解决方案中定义了服务,请尝试构建解决方案并再次添加服务引用。

我已经有了客户端代码,但是如果我不能让Add Service Reference工作,那么客户端代码肯定不会工作。

上面的代码作为Windows ServiceOnStart 事件的一部分开始。

我做错了什么?

更新

答案解决了上面的直接问题,但是我的真实代码使用类似于服务器端的代码,我收到一个错误。我根据答案和一些调试修改了代码。当前代码是:

    public static Boolean ConnectToService()
    {
        ComAppToService.IsPipeAppToService = false;
        ComAppToService.IpcPipeFactory = null;
        Program.HostIpcAppToService = null;
        try
        {
            // Instantiate: Callack
            var ipcCallback = new IpcAppToServiceBack();
            InstanceContext ipcCallbackInstance = new InstanceContext(ipcCallback);

            // Instantiate: Endpoint
            Uri ipcUri = new Uri($"http://10.10.70.134:8000/mex");
            EndpointAddress ipcEndpoint = new EndpointAddress(ipcUri);

            // Instantiate: Binding
            // https://stackoverflow.com/questions/464707/maximum-array-length-quota
            WSDualHttpBinding ipcBindingHttp = new WSDualHttpBinding()
            {
                Security = new WSDualHttpSecurity() { Mode = WSDualHttpSecurityMode.None },
                MaxBufferPoolSize = 6553600,
                MaxReceivedMessageSize = 6553600,
                ClientBaseAddress = ipcUri,
                ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
                {
                    MaxArrayLength = 2147483647,
                    MaxBytesPerRead = 2147483647,
                    MaxDepth = 2147483647,
                    MaxNameTableCharCount = 2147483647,
                    MaxStringContentLength = 2147483647,
                },
            };

            // Instantiate: Factory
            ComAppToService.IpcPipeFactory = new DuplexChannelFactory<IIpcAppToService>(ipcCallbackInstance, ipcBindingHttp, ipcEndpoint);
            Program.HostIpcAppToService = ComAppToService.IpcPipeFactory.CreateChannel();

            // Open: Callback
            Program.HostIpcAppToService.OpenCallback(IpcCallbackDest.App);
            ComAppToService.IsPipeAppToService = true;

            return true;
        }

        catch (Exception ex)
        {
            // Log the exception.
            Debug.WriteLine(ex.Message);
        }

        return false;
    }

例外:

InnerException = {"带有动作的消息 'http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequence' 不能是 由于 ContractFilter 不匹配,在接收方处理 端点调度程序。这可能是因为合同不匹配 (不匹配的操作...

更新 2(反映更新的答案)

使用NetTcpBinding 会产生此错误:

使用 URI:

Uri ipcUri = new Uri($"net.tcp://10.10.70.134:8004");

生成

Message = "There was no endpoint listening at net.tcp://jmr-engineering:8004/ that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details."

使用 URI:

Uri ipcUri = new Uri($"net.tcp://10.10.70.134:8000");

生成

Message = "You have tried to create a channel to a service that does not support .Net Framing. It is possible that you are encountering an HTTP endpoint."

InnerException = {"Expected record type 'PreambleAck', found '72'."}

答案:

接受的答案需要修改。亚伯拉罕没有正确的URI,但他将我指向SvcUtil.exe,它给了我。

URI:

net.tcp://10.10.70.134:8004/StampIpcAppToService

我确实需要NetTcpBinding,所以最终的客户端代码是:

    /// <summary>
    /// Connects the ipc application to service.
    /// </summary>
    /// <returns>Boolean.</returns>
    public static Boolean ConnectToService()
    {
        ComAppToService.IsPipeAppToService = false;
        ComAppToService.IpcPipeFactory = null;
        Program.HostIpcAppToService = null;
        try
        {
            // Instantiate: Callack
            var ipcCallback = new IpcAppToServiceBack();
            InstanceContext ipcCallbackInstance = new InstanceContext(ipcCallback);

            // Instantiate: Endpoint
            Uri ipcUri = new Uri($"net.tcp://10.10.70.134:8004/StampIpcAppToService");
            EndpointAddress ipcEndpoint = new EndpointAddress(ipcUri);

            // Instantiate: Binding
            // https://stackoverflow.com/questions/464707/maximum-array-length-quota
            NetTcpBinding ipcBindingHttp = new NetTcpBinding()
            {
                Security = new NetTcpSecurity() { Mode = SecurityMode.None },
                MaxBufferPoolSize = 6553600,
                MaxReceivedMessageSize = 6553600,
                MaxBufferSize = 6553600,
                MaxConnections = 1000,
                ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
                {
                    MaxArrayLength = 2147483647,
                    MaxBytesPerRead = 2147483647,
                    MaxDepth = 2147483647,
                    MaxNameTableCharCount = 2147483647,
                    MaxStringContentLength = 2147483647,
                },
            };

            // Instantiate: Factory
            ComAppToService.IpcPipeFactory = new DuplexChannelFactory<IIpcAppToService>(ipcCallbackInstance, ipcBindingHttp, ipcEndpoint);
            Program.HostIpcAppToService = ComAppToService.IpcPipeFactory.CreateChannel();

            // Open: Callback
            Program.HostIpcAppToService.OpenCallback(IpcCallbackDest.App);
            ComAppToService.IsPipeAppToService = true;

            return true;
        }

        catch (Exception ex)
        {
            // Log the exception.
            Debug.WriteLine(ex.Message);
        }

        return false;
    }

这是来自SvcUtil.exeoutput.config 的输出。 IpcAppToService.cs 文件没用,但配置文件绝对有用。

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <system.serviceModel>
        <bindings>
            <netTcpBinding>
            <binding name="NetTcpBinding_IIpcAppToService">
                <security mode="None" />
            </binding>
            </netTcpBinding>
        </bindings>
        <client>
            <endpoint address="net.tcp://localhost:8004/StampIpcAppToService"
            binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IIpcAppToService"
            contract="IIpcAppToService" name="NetTcpBinding_IIpcAppToService" />
        </client>
        </system.serviceModel>
    </configuration>

【问题讨论】:

    标签: c# winforms wcf


    【解决方案1】:

    添加服务引用依赖于服务元数据地址而不是服务地址。根据您的上述设置,您的添加服务引用对话框中的 Uri 应该是,

    http://10.10.70.134:8000/mex

    通常,我们必须通过添加Mex 端点或ServiceMetedataBehaviorHttpGetEnabled 属性来公开元数据。随后,客户端可以使用SVCUtil工具生成客户端代理。
    https://docs.microsoft.com/en-us/dotnet/framework/wcf/accessing-services-using-a-wcf-client
    Visual Studio 中,Adding Service reference 对话框是生成客户端代理的快捷方式。它依赖于Mex 服务端点。 Mex 端点地址是快捷方式所必需的。
    如果有什么可以帮助的,请随时告诉我。
    已更新。
    双工通信意味着我们应该在客户端使用Nettcpbinding(支持双工)。

    class Program
        {
            static void Main(string[] args)
            {
                NetTcpBinding binding = new NetTcpBinding()
                {
                    Security = new NetTcpSecurity
                    {
                        Mode = SecurityMode.None
                    },
                    MaxBufferPoolSize = 6553600,
                    MaxBufferSize = 6553600,
                    MaxReceivedMessageSize = 6553600,
                    MaxConnections = 1000,
                    ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
                    {
                        MaxArrayLength = 2147483647,
                        MaxBytesPerRead = 2147483647,
                        MaxDepth = 2147483647,
                        MaxNameTableCharCount = 2147483647,
                        MaxStringContentLength = 2147483647,
                    },
                };
                //replace it with your practical service address.
                Uri uri = new Uri("net.tcp://10.157.13.69:8004");
                ChannelFactory<IService> factory = new ChannelFactory<IService>(binding, new EndpointAddress(uri));
                IService service = factory.CreateChannel();
                var result = service.Test();
                Console.WriteLine(result);
    
            }
    
        }
    
        //the service contract shared between the client-side and the server-side.
        [ServiceContract]
        public interface IService
        {
            [OperationContract]
            string Test();
    
        }
    

    【讨论】:

    • 答案确实解决了所述直接问题,但我的代码使用与服务器端代码对应的客户端,而不是添加服务参考,请参阅添加到我的问题的客户端代码。我收到声明的例外情况。我根据答案和调试修改了客户端。 VS 声明我必须使用Dual 而不是Basic。不过,我得到了一个错误。
    • @SarahWeinberger ,请参阅更新后的回复。在调用 SOAP 服务时,我们应该保持客户端和服务端的绑定一致,这样可以保证通信通道的建立。
    猜你喜欢
    • 2018-06-16
    • 1970-01-01
    • 2015-01-30
    • 1970-01-01
    • 2011-11-10
    • 1970-01-01
    • 2014-01-21
    • 2022-08-16
    • 2011-03-30
    相关资源
    最近更新 更多