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