Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在客户端连接到WCF服务时观察超时异常-两者都驻留在同一应用程序中_C#_.net_Wcf - Fatal编程技术网

C# 在客户端连接到WCF服务时观察超时异常-两者都驻留在同一应用程序中

C# 在客户端连接到WCF服务时观察超时异常-两者都驻留在同一应用程序中,c#,.net,wcf,C#,.net,Wcf,我有一个应用程序,它正在托管WCF服务。一些信息: 它必须是一个单例应用程序——这意味着它的另一个实例不能并行运行。现在,假设下面的示例表单应用程序就是这样设计的 它也需要充当客户 它使用net.tcp绑定 它使用SecurityMode=Transport 但当我从客户端调用方法时,服务器并没有得到调用。当调用“CallServer”方法时,我看到一个“超时异常” 如果您建议更改InstanceContextMode或ConcurrencyMode或SessionMode的设置,请先自己尝

我有一个应用程序,它正在托管WCF服务。一些信息:

  • 它必须是一个单例应用程序——这意味着它的另一个实例不能并行运行。现在,假设下面的示例表单应用程序就是这样设计的
  • 它也需要充当客户
  • 它使用net.tcp绑定
  • 它使用SecurityMode=Transport
但当我从客户端调用方法时,服务器并没有得到调用。当调用“CallServer”方法时,我看到一个“超时异常”

如果您建议更改
InstanceContextMode
ConcurrencyMode
SessionMode
的设置,请先自己尝试,因为我已经尝试了这些组合,但没有任何帮助

这里的代码是一个示例,但可以用于测试。要使用此代码,请在表单上创建两个按钮: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();
        }
    }
}
使用系统;
使用System.ServiceModel;
使用System.Windows.Forms;
命名空间Windows窗体应用程序1{
公共部分类Form1:Form{
服务主机(ServiceHost);;
const string URI=“net。tcp://localhost:8602/MyService";
公共表格1(){
初始化组件();
this.FormClosing+=委托{if(_host!=null)_host.Close(new TimeSpan(0,1,0));};
}
私有void btnHostService_单击(对象发送方,事件参数e){
//不要再主持了
if(_host!=null)返回;
_主机=新的ServiceHost(typeof(ContractServer),新的Uri(Uri));
var binding=newnettcppbinding(SecurityMode.Transport){PortSharingEnabled=true};
binding.Security.Transport.ClientCredentialType=TcpClientCredentialType.Windows;
_AddServiceEndpoint(typeof(IContract),binding,string.Empty);
_host.Opened+=委托{MessageBox.Show(这是“托管服务”);};
_host.Open();
}
私有void btnClientInvoke\u单击(对象发送方,事件参数e){
var binding=newnettcppbinding(SecurityMode.Transport){PortSharingEnabled=true};
binding.Security.Transport.ClientCredentialType=TcpClientCredentialType.Windows;
////将它们全部设置为1分钟-这是默认值
//binding.OpenTimeout=newtimespan(0,1,0);
//binding.SendTimeout=新的时间跨度(0,1,0);
//binding.CloseTimeout=新的时间跨度(0,1,0);
ContractClient=null;
试一试{
client=newContractClient(绑定,新端点地址(URI));
client.CallServer();
}
捕获(异常exc){
Show(exc.ToString());
}
如果(客户端!=null){
试一试{
client.Close();
}
捕获(异常exc){
Show(exc.ToString());
client.Abort();
}
}
}
私有作废Form1\u FormClosing(对象发送方,FormClosingEventArgs e){
如果(_host!=null)
_关闭(新的时间跨度(0,1,0));
}
}
[ServiceContract(SessionMode=SessionMode.Allowed)]
公共接口IContract{
[运营合同(IsOneWay=true)]
void CallServer();
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single,ConcurrencyMode=ConcurrencyMode.Multiple,IncludeExceptionDetailInFaults=true)]
公共类ContractServer:IContract{
public void CallServer(){
Show(“客户呼叫!”);
}
}
公共类ContractClient:System.ServiceModel.ClientBase,IContract{
public ContractClient(System.ServiceModel.Channel.Binding绑定,System.ServiceModel.EndpointAddress远程地址):基(绑定,远程地址){}
public void CallServer(){
base.Channel.CallServer();
}
}
}
更新1
信息提示:修复方法是在
ContractServer
中使用“UseSynchronizationContext=false”,并在主机绑定中禁用端口共享。但我不知道为什么。

你可能会陷入僵局

在应用程序中托管
WCF
服务时,它使用该应用程序的同步上下文。在本例中,一个
WinForms
app是一个单线程同步上下文

因此,当您的“客户机”调用您的“服务器”时,它会被阻塞,直到收到响应,“服务器”无法发送该响应,因为单个线程被“客户机”阻塞,因此会出现死锁

要修复此问题,您需要告诉服务不要使用该同步上下文:

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

在您的真实代码中,您还将服务托管在WinForms应用程序中?是的,它是winform应用程序,具有复杂的性质。尝试过,但没有成功。但这个理论可能是正确的。只是提议的解决方案不起作用。@Nayan如果没有端口共享,它确实起作用。为什么要使用端口共享?好的!成功了!!我使用它是因为我不想阻塞端口。但是,你能解释一下禁用端口共享会发生什么吗?@Nayan,我真的不知道。我认为端口共享服务找不到合适的侦听器,但我真的不知道为什么。