//WCF .Net Framework 3.5出的
//支持多种协议:http tcp IPC MSMQ p2p
//支持多种宿主:控制台 网站 winform windows-service
//双工
//1 接口是契约,要有特性ServiceContract
// 方法上有特性OperationContract
//2 出参返回实体如果没有任何标记,返回所有属性,但实体必须有无参构造函数
// 如果没有无参构造函数,需要在类上加特性DataContractAttribute;属性加特性[DataMember]后才会返回
//WCF托管到控制台:
//1.配置文件中配置behavior、binding、service(address+binding+contract)
//2.ServiceHost实例Open(需要管理员权限)
//3.服务引用时 可能需要设置Windows/Temp文件夹给IIS_User的权限
//Tcp协议不能穿防火墙(需要授权),有状态
//一般WCF+WindowsService 随机启动
//WCF双工:相互通信,可以实现IM
//用tcp协议 通过回调来实现
//1 服务端契约标记需要加上回调接口 特性[ServiceContract(CallbackContract = typeof(ICallback))]
// 其中ICallback是用户自己定义的回调接口
// 方法上的[OperationContract(IsOneWay = true)]表明是相当于异步式的,请求后异步接收结果
//2 客户端调用服务时必须实现ICallback接口,ICallback只能有一个方法,两个回调的时候出错了
// 调用时把回调方法传入
//webService和WCF泛型返回值不可以
//WCF安全
//1 带上口令--请求时带上参数
//2 SoapHeader
//3 Windows 身份验证
//4 X509证书
Console.WriteLine("Hello World!");
try
{
ServiceInit.Process();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.Read();
服务初始化&启动
using MySOA.WCF.Service;
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Text;
namespace MySOA.Project
{
public class ServiceInit
{
public static void Process()
{
//ServiceHost 服务主机
List<ServiceHost> hosts = new List<ServiceHost>()
{
new ServiceHost(typeof(MathService)),
new ServiceHost(typeof(CalculatorService)),//双工
};
foreach (var host in hosts)
{
host.Opening += (s, e) => Console.WriteLine($"{host.GetType().Name} 准备打开");
host.Opened += (s, e) => Console.WriteLine($"{host.GetType().Name} 已经打开");
host.Closing += (s, e) => Console.WriteLine($"{host.GetType().Name} 准备关闭");
host.Closed += (s, e) => Console.WriteLine($"{host.GetType().Name} 已经关闭");
host.Open();
}
Console.WriteLine("输入任意字符停止服务");
Console.ReadKey();
foreach (var host in hosts)
{
host.Close();
}
Console.ReadKey();
}
}
}
契约&回调约束类
using MySOA.WCF.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace MySOA.WCF.Interface
{
[ServiceContract]
public interface IMathService
{
[OperationContract]
int PlusInt(int x, int y);
[OperationContract]
int Minus(int x, int y);
[OperationContract]
UserWCF GetUser(int age);
[OperationContract]
List<UserWCF> GetUserList();
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace MySOA.WCF.Interface
{
[ServiceContract(CallbackContract = typeof(ICallback))]
public interface ICalculatorService
{
[OperationContract(IsOneWay = true)]
void PlusInt(int x, int y);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
namespace MySOA.WCF.Interface
{
/// <summary>
/// 普通接口,由客户端实现
/// </summary>
public interface ICallback
{
[OperationContract(IsOneWay = true)]
void Show(int x, int y, int rst);
//void Show2(int x, int y, int rst);
}
}
服务类——实现契约
using MySOA.WCF.Interface;
using MySOA.WCF.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MySOA.WCF.Service
{
public class MathService : IMathService
{
public UserWCF GetUser(int age)
{
return new UserWCF()
{
Id = 1,
Name = "小王",
Age = 18,
Sex = 1,
Discription = "帅"
};
}
public List<UserWCF> GetUserList()
{
return new List<UserWCF>()
{
new UserWCF()
{
Id = 1,
Name = "小王",
Age = 18,
Sex = 1,
Discription = "帅"
},
new UserWCF()
{
Id = 2,
Name = "小高",
Age = 18,
Sex = 0,
Discription = "美"
}
};
}
public int Minus(int x, int y)
{
return x + y;
}
public int PlusInt(int x, int y)
{
return x - y;
}
}
}
using MySOA.WCF.Interface;
using MySOA.WCF.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MySOA.WCF.Service
{
public class CalculatorService : ICalculatorService
{
public void PlusInt(int x, int y)
{
int rst = x + y;
Thread.Sleep(2000);
ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();
callback.Show(x, y, rst);
//callback.Show2(x, y, rst);
}
}
}
服务引用
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MySOA.WCFTcpDuplex.TestProject
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"双工调用开始 ThreadId:{Thread.CurrentThread.ManagedThreadId}");
MyConsoleWCFTcpDuplex.CalculatorServiceClient clent = null;
try
{
//实现回调,并在请求时把回调传入,回调结果会由不同线程接收
InstanceContext callbackInstance = new InstanceContext(new CustomeCallback());
clent = new MyConsoleWCFTcpDuplex.CalculatorServiceClient(callbackInstance);
clent.PlusInt(1, 23);
Console.ReadKey();//通道保持
clent.Close();
Console.WriteLine($"双工调用结束 ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
catch (Exception ex)
{
if (clent != null)
clent.Abort();//如果调用过程中断网,上面的步骤会出现异常,这能保证WCF被释放掉
Console.WriteLine(ex.ToString());
}
}
}
}
双工客户端需要实现服务端的回调接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MySOA.WCFTcpDuplex.TestProject
{
public class CustomeCallback : MyConsoleWCFTcpDuplex.ICalculatorServiceCallback
{
public void Show(int x, int y, int rst)
{
Console.WriteLine($"回调:{x}+{y}={rst} ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
//public void Show2(int x, int y, int rst)
//{
// Console.WriteLine($"回调2:{x}+{y}={rst} ThreadId:{Thread.CurrentThread.ManagedThreadId}");
//}
}
}
控制台寄宿时配置
<?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="MathServiceBehavior">
<!-- httpGetEnabled - bool类型的值,表示是否允许通过HTTP的get方法获取sevice的WSDL元数据 -->
<serviceMetadata httpGetEnabled="false"/>
<serviceDebug httpHelpPageEnabled="false"/>
<serviceTimeouts transactionTimeout="00:10:00"/>
<serviceThrottling maxConcurrentCalls="1000" maxConcurrentSessions="1000" maxConcurrentInstances="1000" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<!--http-->
<basicHttpBinding>
<binding name="httpbinding">
</binding>
</basicHttpBinding>
<!--tcp-->
<netTcpBinding>
<binding name="tcpbinding">
<security mode="None">
<transport clientCredentialType="None" protectionLevel="None" />
</security>
</binding>
</netTcpBinding>
</bindings>
<services>
<!--<service name="MySOA.WCF.Service.MathService" behaviorConfiguration="MathServiceBehavior">
<host>
<baseAddresses>
--><!-- 每种传输协议的baseAddress,用于跟使用同样传输协议Endpoint定义的相对地址组成完整的地址,
每种传输协议只能定义一个baseAddress。HTTP的baseAddress同时是service对外发布服务说明页面的URL --><!--
<add baseAddress="http://localhost:8009/MathService"/>
</baseAddresses>
</host>
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="httpbinding" contract="MySOA.WCF.Interface.IMathService">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
--><!-- 此终结点不使用安全绑定,应在部署前确保其安全或将其删除--><!--
--><!--<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>--><!--
</service>-->
<!--tcp-->
<service name="MySOA.WCF.Service.MathService" behaviorConfiguration="MathServiceBehavior">
<host>
<baseAddresses>
<!-- 每种传输协议的baseAddress,用于跟使用同样传输协议Endpoint定义的相对地址组成完整的地址,
每种传输协议只能定义一个baseAddress。HTTP的baseAddress同时是service对外发布服务说明页面的URL -->
<add baseAddress="net.tcp://localhost:8010/MathService"/>
</baseAddresses>
</host>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
<endpoint address="" binding="netTcpBinding" bindingConfiguration="tcpbinding" contract="MySOA.WCF.Interface.IMathService">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<!-- 此终结点不使用安全绑定,应在部署前确保其安全或将其删除-->
<!--<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>-->
</service>
<!--tcp双工-->
<service name="MySOA.WCF.Service.CalculatorService" behaviorConfiguration="MathServiceBehavior">
<host>
<baseAddresses>
<!-- 每种传输协议的baseAddress,用于跟使用同样传输协议Endpoint定义的相对地址组成完整的地址,
每种传输协议只能定义一个baseAddress。HTTP的baseAddress同时是service对外发布服务说明页面的URL -->
<add baseAddress="net.tcp://localhost:8011/CalculatorService"/>
</baseAddresses>
</host>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
<endpoint address="" binding="netTcpBinding" bindingConfiguration="tcpbinding" contract="MySOA.WCF.Interface.ICalculatorService">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<!-- 此终结点不使用安全绑定,应在部署前确保其安全或将其删除-->
<!--<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>-->
</service>
</services>
</system.serviceModel>
</configuration>