项目基本结构
图 1 的 Client 工程对应图 2 的 Client App。
图 1 的 HelloWorldService 工程对应图 2 的 Service,具体的 WCF 服务是由 HelloWorldService 提供的。
图 1 的 Host 工程对应图 2 的 ServiceHost,要为HelloWorldService这个服务来提供一个运行的环境。
图2:WCF 架构
1.WCF 服务创建
- 由 HelloWorldService 工程实现 WCF 服务创建
- 为了能够使用WCF,在引用里面需要添加
System.ServiceModel
命名空间 - 接口定义了服务要完成哪些事情,在这个接口中要完成 GetHelloWorld 这个操作并且要有一个字符串类型的返回值
- 在接口上面多了一些属性
ServiceContract
和OperationContract
,这些属性是在WCF当中特有的一些标记,这些标记我们把它称之为契约,在 WCF 中客户端和服务端进行通信必须要遵循某个契约来完成,这个实际上是WCF一个非常显著的特点,客户端和服务端保证契约必须相吻合
1:IHelloWorldService.cs
using System;
using System.ServiceModel;
namespace HelloWorldService
{
/*加了契约的接口*/
[ServiceContract(Namespace="http://www.monkeyfu.net/")]
public interface IHelloWorldService
{
[OperationContract]
string GetHelloWorld();
}
}
- HelloWorldService.cs
using System;
using System.ServiceModel;
namespace HelloWorldService
{
public class HelloWorldService : IHelloWorldService
{
public string GetHelloWorld()
{
return string.Format("消息收到了在{0}:{1}", DateTime.Now, "Hello World!");
}
}
}
2. 托管 WCF 服务
- 由 Host 工程托管 WCF 服务
- Host 工程放在服务的主机里执行
- Host 工程创建步骤:
- 创建主机:
- 在 Main 主函数里,我们创建一个
ServiceHost host = new ServiceHost()
,也就是创建一个主机,这个 host 所对应的服务实例是 HelloWorldService 工程里面实现的那个 HelloWorldService 类。 -
ServiceHost host = new ServiceHost(typeof(HelloWorldService.HelloWorldService))
这句代码的意思是我们启动了一个新的主机,这个主机里面现在运行着一个服务,这个服务是由HelloWorldService.HelloWorldService
这个类来进行实现的。
- 添加新的 Endpoint:
- 创建完一个 host 我们需要添加一个新的 Endpoint,Endpoint叫端点也叫终结点,它的主要作用是用来提供 Service 向外发布的接口以实现客户端和服务端之间的通信。
-
System.ServiceModel
中的ServiceHost AddServiceEndpoint()
用于添加一个新的Endpoint,参数说明:-
第一个参数
System.Type implementedContract
:
指定 Endpoint 绑定到了哪个接口上。在这里是绑定到了IHelloWorldService这个接口上面,也就是说所有通过我们添加的这个 Endpoint 的消息必须都要遵循 IHelloWorldService 接口里面相应的契约当中的要求 -
第二个参数
Binding binding
:
指定 Endpoint 传输消息的时候需要绑定在哪个协议上。在这里是使用new NetTcpBinding()
, 就是使用 TCP 的协议来进行消息的传递 -
第三个参数
string address
:
指定在进行消息传递的时候,通信的具体地址是哪里。在这里是net.tcp://localhost:9000/HelloWorldService
-
返回值
ServiceEndpoint
-
-
打开主机:
然后host.Open();
打开主机,当 host 打开后就可以来提供服务了,客户端也可以连接到主机上面来使用主机上面提供的这个 HelloWorldService 服务。
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using HelloWorldService;
namespace Host
{
class Program
{
static void Main(string[] args)
{
//step1 启动了一个新的主机,这个主机里面现在运行着一个服务
using (ServiceHost host = new ServiceHost(typeof(HelloWorldService.HelloWorldService)))
{
//step2 用来提供Service向外发布的接口以实现客户端和服务端之间的通信
host.AddServiceEndpoint(typeof(IHelloWorldService), new NetTcpBinding(), "net.tcp://localhost:9000/HelloWorldService");
//step3 打开主机
host.Open();
Console.ReadLine();
}
}
}
}
3. WCF 服务的调用
-
由 Client 工程调用 WCF 服务
-
步骤:
- 创建 Proxy 代理:
- Client 想要与 Service 进行交互的话需要一个 Proxy 代理。
- ChannelFactory通道厂类本身是一个泛型,在这个泛型指明这个Proxy遵循了哪一个契约,在这里我们使用了 IHelloWorldService 这个契约,这个契约的描述我们定义在了class Program的上面,和我们定义的IHelloWorldService 描述是一摸一样的,客户端要想使用这个服务的话,必须使用和这个服务相匹配的契约来进行访问,这个是必须要遵守的要求。
- 使用
ChannelFactory<TChannel>.CreatChannel()
方法可以实现创建一个Proxy,参数说明:- 第一个参数
Binding binding
:
指定 Endpoint 传输消息的时候需要绑定在哪个协议上。同样使用 TCP 的协议来进行消息的传递 - 第二个参数
EndpointAddress endpointAddress
:
指定我们在进行消息传递的时候,通信的具体地址是哪里。这里创建一个新的 EndpointAddress,并且它的地址是指向 ServiceHost 的地址,所以当我们创建完 Proxy 代理以后,实际上是把我们的 Client 端和 Host 端建立的一个等价的联系,这个联系就是我们大家都是使用 Tcp 协议,来访问 Host 提供的地址net.tcp://localhost:9000/HelloWorldService
,并且使用相同的契约 - 返回值
TChannel
- 第一个参数
-
通过代理访问服务:
在创建完 Proxy 代理以后,客户端就可以调用 proxy 代理里面的 GetHelloWorld 这个方法了,这个 GetHelloWorld 就是前面的 IHelloWorldService 接口提供的方法。在执行 GetHelloWorld 的这个方法时候, WCF 会通过 Tcp 协议来访问 Host 里面net.tcp://localhost:9000/HelloWorldService
这个地址,在 Host 端的 Endpoint 会接收这个消息以后根据契约 IHelloWorldService 找到 HelloWorldService 类里面相应的业务逻辑,并且执行逻辑里面实际的代码。对于客户端这些底层服务端不需要去关心,只需要调用 GetHelloWorld 这个方法就行,其实就是通过代理类完成类似本地调用,实现了分布式调用。
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
namespace Client
{
//step1 创建服务契约的一个副本
[ServiceContract(Namespace = "http://www.monkeyfu.net/")]
public interface IHelloWorldService
{
[OperationContract]
string GetHelloWorld();
}
class Program
{
static void Main(string[] args)
{
//step2 创建Proxy代理
IHelloWorldService proxy = ChannelFactory<IHelloWorldService>.CreateChannel(new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:9000/HelloWorldService"));
//step3 通过代理访问服务
string Result = proxy.GetHelloWorld();
Console.WriteLine(Result);
Console.ReadLine();
}
}
}
Client端通过Tcp协议去访问了Host端里面的HelloWorldService服务里面的GetHelloWorld这个方法, 这样我们就完成了一个简单的基于Tcp协议的分布式的调用:
- 首先启动 Host 端
-
然后启动 Client 端
运行时效果
总结
- 服务端:
- 定义和实现服务契约:
在服务端,不但要完成具体服务的业务逻辑,还要完成这个服务相应的契约 - 为服务类型创建 ServiceHost 实例,暴露 Endpoint:
创建 ServiceHost 实例可以理解为我们创建了一个服务,并把这个服务放到服务器里来处理,同时要暴露出 Endpoint 端点,使得客户端来访问 ServiceHost 里面对应的 Service 服务 - 打开通信的通道:
使得客户端能够访问服务端的这些服务
- 客户端
- 需要服务契约的一个副本和关于 Endpoints 端点的信息:
服务契约的一个副本可以理解为在这个服务当中包含的契约的一个接口,虽然说客户端不需要服务的具体的业务逻辑,但是客户端需要这个服务当中包含契约的一个接口,来告诉客户端它如何与服务端进行通信应该遵循哪些规则,Endpoint端点的信息主要包含了服务器端服务的具体地址以及使用什么样的协议进行具体的通信 - 为特定的Endpoints构建通信通道并且执行调用操作已完成我们具体的服务:
下面这个图三更好的说明了客户端和服务端具体的Endpoint的一个作用
我们就以ABC来进行一下阐述,一个消息要想传送出去或者想要接收到的话必须要有ABC这三样东西的,这三样是缺一不可的:
-
A表示Address地址,这个主机在哪,像上面提到的”net.tcp://localhost:9000/HelloIndigo“,这个就是我们所说的地址,它告诉客户端要去哪里访问主机当中的服务
-
B表示Binding绑定,Binding告诉我们如何来进行通信,像上面提到的我们使用的Tcp的协议来进行通信
-
C表示Contract契约,Contract告诉我们究竟要传什么,要干什么,还对我们要干什么的操作进行了具体的约束,我们做这件事情的名称和参数都是用Contract契约来描述的
图 3
网友评论