当客户端连接到WCF服务时观察到超时异常-两者都位于同一应用程序中

本文关键字:两者都 于同一 应用程序 异常 观察 连接 客户端 WCF 服务 超时 | 更新日期: 2023-09-27 18:04:09

我有一个托管WCF服务的应用程序。一些信息:

  • 它必须是一个单例应用程序-意味着它的另一个实例不能并行运行。现在,假设下面的示例表单应用程序是这样设计的:
  • 它也需要作为客户端。
  • 它使用net。tcp绑定
  • 使用SecurityMode = Transport

但是当我从客户端调用方法时,服务器没有得到调用。当调用CallServer方法时,我看到一个"超时异常"。

请,如果您建议更改InstanceContextModeConcurrencyModeSessionMode的设置,请先自己尝试,因为我已经尝试过组合,它们没有帮助。

这里的代码是一个示例,但可以用于测试。要使用此代码,请在表单上创建2个按钮:btnHostService和btnClientInvoke。

using System;
using System.ServiceModel;
using System.Windows.Forms;
namespace WindowsFormsApplication1 {
    public partial class Form1 : Form {
        ServiceHost _host;
        const string URI = "net.tcp://localhost:8602/MyService";
        public Form1() {
            InitializeComponent();
            this.FormClosing += delegate { if (_host != null) _host.Close(new TimeSpan(0, 1, 0)); };
        }
        private void btnHostService_Click(object sender, EventArgs e) {
            //don't host again
            if (_host != null) return;
            _host = new ServiceHost(typeof(ContractServer), new Uri(URI));
            var binding = new NetTcpBinding(SecurityMode.Transport) { PortSharingEnabled = true };
            binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
            _host.AddServiceEndpoint(typeof(IContract), binding, string.Empty);
            _host.Opened += delegate { MessageBox.Show(this, "Service Hosted"); };
            _host.Open();
        }
        private void btnClientInvoke_Click(object sender, EventArgs e) {
            var binding = new NetTcpBinding(SecurityMode.Transport) { PortSharingEnabled = true };
            binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
            ////set all of them to 1 min - which is default
            //binding.OpenTimeout = new TimeSpan(0, 1, 0);
            //binding.SendTimeout = new TimeSpan(0, 1, 0);
            //binding.CloseTimeout = new TimeSpan(0, 1, 0);
            ContractClient client = null;
            try {
                client = new ContractClient(binding, new EndpointAddress(URI));
                client.CallServer();
            }
            catch (Exception exc) {
                MessageBox.Show(exc.ToString());
            }
            if (client != null) {
                try {
                    client.Close();
                }
                catch (Exception exc) {
                    MessageBox.Show(exc.ToString());
                    client.Abort();
                }
            }
        }
        private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
            if (_host != null)
                _host.Close(new TimeSpan(0, 1, 0));
        }
    }
    [ServiceContract(SessionMode = SessionMode.Allowed)]
    public interface IContract {
        [OperationContract(IsOneWay = true)]
        void CallServer();
    }
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, IncludeExceptionDetailInFaults = true)]
    public class ContractServer : IContract {
        public void CallServer() {
            MessageBox.Show("Client called!");
        }
    }
    public class ContractClient : System.ServiceModel.ClientBase<IContract>, IContract {
        public ContractClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : base(binding, remoteAddress) { }
        public void CallServer() {
            base.Channel.CallServer();
        }
    }
}

更新1

For info: fix是在ContractServer中使用"usesynsynchronationcontext = false",并在主机绑定中禁用端口共享。但我不知道为什么。

当客户端连接到WCF服务时观察到超时异常-两者都位于同一应用程序中

您可能出现死锁。

当您在应用程序中托管WCF服务时,它使用该应用程序的同步上下文。在这种情况下,WinForms应用程序,单线程同步上下文。

所以当你的"客户端"调用你的"服务器"时,它会被阻塞,直到它得到响应,而"服务器"不能发送响应,因为单线程被"客户端"阻塞,因此死锁。

要解决这个问题,你需要告诉服务不要使用同步上下文:

[ServiceBehavior(
    UseSynchronizationContext = false,
    InstanceContextMode = InstanceContextMode.Single, 
    ConcurrencyMode = ConcurrencyMode.Multiple, 
    IncludeExceptionDetailInFaults = true)]
public class ContractServer : IContract 
{
    public void CallServer() 
    {
        MessageBox.Show("Client called!");
    }
}