当客户端连接到WCF服务时观察到超时异常-两者都位于同一应用程序中
本文关键字:两者都 于同一 应用程序 异常 观察 连接 客户端 WCF 服务 超时 | 更新日期: 2023-09-27 18:04:09
我有一个托管WCF服务的应用程序。一些信息:
- 它必须是一个单例应用程序-意味着它的另一个实例不能并行运行。现在,假设下面的示例表单应用程序是这样设计的:
- 它也需要作为客户端。
- 它使用net。tcp绑定
- 使用SecurityMode = Transport
但是当我从客户端调用方法时,服务器没有得到调用。当调用CallServer方法时,我看到一个"超时异常"。
请,如果您建议更改InstanceContextMode
或ConcurrencyMode
或SessionMode
的设置,请先自己尝试,因为我已经尝试过组合,它们没有帮助。
这里的代码是一个示例,但可以用于测试。要使用此代码,请在表单上创建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
服务时,它使用该应用程序的同步上下文。在这种情况下,WinForms
应用程序,单线程同步上下文。
所以当你的"客户端"调用你的"服务器"时,它会被阻塞,直到它得到响应,而"服务器"不能发送响应,因为单线程被"客户端"阻塞,因此死锁。
要解决这个问题,你需要告诉服务不要使用同步上下文:
[ServiceBehavior(
UseSynchronizationContext = false,
InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple,
IncludeExceptionDetailInFaults = true)]
public class ContractServer : IContract
{
public void CallServer()
{
MessageBox.Show("Client called!");
}
}