wcf的双工通讯

勇哥注:

wcf的双工通讯,有双工通讯和发布订阅两种方式。


(一)勇哥先来演示“双工通讯”

程序还是使用简单的add计算的例子。

下面是程序的解决方案,为了简单就只有两个项目。

我们把契约、服务、hosting合在一起,变成下面的WcfService项目。

image.png


ContractAndService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace WcfService
{
	[ServiceContract(Namespace = "http://47.98.154.65/", CallbackContract =typeof(ICallback))]
	public interface ICal
	{
		[OperationContract(IsOneWay =true)]
		void Add(int d1, int d2);
	}

	public class Cal : ICal
	{
		public void Add(int d1, int d2)
		{
			ICallback back=OperationContext.Current.GetCallbackChannel<ICallback>();
			back.DisplayCalRes(d1, d2, d1 + d2);
		}
	}

	public interface ICallback
	{
		[OperationContract(IsOneWay = true)]
		void DisplayCalRes(int d1, int d2,int res);
	}
}

program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace WcfService
{
	internal class Program
	{
		
		static void Main(string[] args)
		{
			using (ServiceHost host = new ServiceHost(typeof(WcfService.Cal)))
			{
				host.Opened += delegate
				{
					Console.WriteLine("Cal服务已经启动");
				};
				host.Open();
				Console.ReadKey();
			}
			
		}
	}
}

服务端app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
    <system.serviceModel>
        <behaviors>
            <serviceBehaviors>
                <behavior name="NewBehavior0">
                    <serviceDebug includeExceptionDetailInFaults="true" />
                    <serviceMetadata httpGetEnabled="true" httpGetUrl="http://127.0.0.1:9997/Cal" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <services>
            <service behaviorConfiguration="NewBehavior0" name="WcfService.Cal">
                <endpoint address="net.tcp://127.0.0.1:9998/Cal" binding="netTcpBinding"
                    bindingConfiguration="" contract="WcfService.ICal" />
            </service>
        </services>
    </system.serviceModel>
</configuration>



客户端app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
    <system.serviceModel>
        <bindings>
            <netTcpBinding>
                <binding name="NetTcpBinding_ICal">
                    <security>
                        <transport sslProtocols="None" />
                    </security>
                </binding>
            </netTcpBinding>
        </bindings>
        <client>
            <endpoint address="net.tcp://127.0.0.1:9998/Cal" binding="netTcpBinding"
                bindingConfiguration="NetTcpBinding_ICal" contract="calfun.ICal"
                name="NetTcpBinding_ICal">
                <identity>
                    <userPrincipalName value="CYE8GDOEVM74V9Y\Administrator" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>


Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WinFormClient
{
	public partial class Form1 : Form
	{
		InstanceContext instance = new InstanceContext(new CallbackFun());
		calfun.CalClient proxy;
		public Form1()
		{
			InitializeComponent();
			proxy =new calfun.CalClient(instance, "NetTcpBinding_ICal");

		}

		private void button1_Click(object sender, EventArgs e)
		{
			proxy.Add(1, 1);
		}
	}

	public class CallbackFun : calfun.ICalCallback
	{
		public void DisplayCalRes(int d1, int d2, int res)
		{
			MessageBox.Show($"{d1}+{d2}={res}");
		}
	}
}

运行效果:

那个弹窗 1+1=2,实际上是由服务端回调的,回调的是客户端定义的这个CallbalFun类的DisplayCalRes函数。

image.png


几个重点:

(1) 注意客户端回调这个类的基类,应该使用“服务引用”的ICalCallback

public class CallbackFun : calfun.ICalCallback

而不应该使用下面的:

public class CallbackFun : WcfService.ICallback

这个搞错了,下场就是下面的报错:

InstanceContext 包含未实现 CallbackContractType“WinFormClient.calfun.ICalCallback”的 UserObject。”

image.png

(2)ICal的Add函数、ICallback的DisplayCalRes函数都设置了操作契约类型为IsOneWay = true。这个不是多余的,是有意义的。

[OperationContract(IsOneWay =true)]

void Add(int d1, int d2);

[OperationContract(IsOneWay = true)]

void DisplayCalRes(int d1, int d2,int res);


对于IsOneWay,勇哥解释一下:

我们知道WCF可以采用三种不同的Message Exchange Pattern(消息交互模式,MEP)——One-way,Request/Response,Duplex。

其实从本质上讲,One-way,Request/Response是两种基本的MEP, Duplex可以看成是这两种MEP的组合——两个One-way,两个Request/Response或者是一个One-way和一个Request/Response。

在定义Service Contract的时候,如果我们没有为某个Operation显式指定为One-way (IsOneWay = true), 

那么默认采用Request/Response方式。


例如下面的函数DisplayResult,它指定了IsOneWay=true,所以它的消息交换模式就是单向。

如果不设置IsOneWay属性,则就是默认的消息交换方式:请求/响应。

    public interface ICallback
    {
        [OperationContract(IsOneWay =true)]
        void DisplayResult(double x, double y, double result);
    }

我们把上面ICal的Add函数、ICallback的DisplayCalRes函数的IsOneWay = true都去掉。

这样就变成默认的Request/Response方式(请求/响应) 方式。

然后做两件事:

1。重新编译服务端,运行服务端

2。更新客户端的服务引用,启动客户端。

点Add,然后你就看到下面的报错:

image.png

这个错误的原因是:

由于Callback Operation是采用Request/Response方式调用的,所以它必须要收到来自Client端Reply来确定操作正常结束。这实际上形成了一个Deadlock(死锁)。


在蒋金楠的文章:https://www.cnblogs.com/artech/archive/2007/03/29/692032.html

里,讲到了这个问题产生的原因。

但是他当年的演示代码是跑出了弹窗结果后,再超时1分钟后才抛出一种超时的异常。

image.png

这种超时异常实际上没有把核心的原因显示清楚的。


蒋金楠他当年用的系统、C#版本、wcf版本、异常配置等许多因素跟勇哥现在的不一样。

现在是2024年,勇哥用的是win10, vs2022环境下,程序直接异常中止了,而且这个异常精准的显示清楚了核心原因是什么。并且还不允许你继续跑下去。

(这说明开发环境的异常检测能力变强了)


演示代码下载:

链接:https://pan.baidu.com/s/1ElJqlCCY_TaGAYwIQQeFEw

提取码:93c0 

--来自百度网盘超级会员V6的分享




本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:
本帖最后由 勇哥,很想停止 于 2024-07-04 14:59:21 编辑

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2025年4月    »
123456
78910111213
14151617181920
21222324252627
282930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864