引用
wxfclnice 的 WCF 本质(3)
6. 端点 (Endpoints)
每个服务都会关联到一个用于定位服务位置的地址(Address),一个用于定义如何与服务进行通讯的绑定(Binding),以及一个告知客户端服务能做什么的契约(Contract),这三样共同组成了服务的助记符 —— ABC。WCF 以端点(Endpoint)这样一个形式来体现三者的关系,端点本身就是地址、契约和绑定的组合体。
端点示意图
每个端点都必须完整拥有这三个组成部分,宿主(host)则通过公开端点来对外提供服务。理论上,端点就是服务的外部交互接口、就像 CLR 或者 COM 接口。每个服务至少需要公开一个端点,服务上所有的端点都必须拥有唯一的定位地址,单个服务可以提供多个端点供不同类型的客户端调用。这些端点可以使用相同或不同的绑定对象,可以拥有相同或不同的服务契约。单个服务的不同端点之间没有任何关联。
端点配置管理
我们可以在宿主配置文件(app.config / web.config)中对端点参数进行设置。
<system.serviceModel>
<services>
<service name = "MyNamespace.MyService">
<endpoint
address = "http://localhost:8000/MyService/"
binding = "wsHttpBinding"
contract = "MyNamespace.IMyContract"
/>
</service>
</services>
</system.serviceModel>
需要注意的是,我们必须指定完整的契约限定名(含 Namespace)。当然,我们可以为单个服务提供多个端点设置。
<service name = "MyService">
<endpoint
address = "http://localhost:8000/MyService/"
binding = "wsHttpBinding"
contract = "IMyContract"
/>
<endpoint
address = "net.tcp://localhost:8001/MyService/"
binding = "netTcpBinding"
contract = "IMyContract"
/>
<endpoint
address = "net.tcp://localhost:8002/MyService/"
binding = "netTcpBinding"
contract = "IMyOtherContract"
/>
</service>
在配置文件中设置服务参数是种非常好的选择,它提供了更好的弹性,可以在不修改代码不重新发布系统的情况下对服务的地址、绑定和契约参数进行修改。
我们还可以提供一个或多个默认的基本地址(Base Address),这样在端点设置中只需提供相对地址。多个基本地址之间不能冲突,不能在同一个端口进行监听。相对地址通过端点绑定类型与基本地址进行匹配,从而在运行时获得完整地址。如果我们将某个端点设置中的地址设为空值(或省略 address),则表示直接使用某个相匹配的基本地址。
<service name = "MyService">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080/" />
<add baseAddress="net.tcp://localhost:8081/" />
</baseAddresses>
</host>
<endpoint
address = "MyService" <!-- http://localhost:8080/MyService -->
binding = "wsHttpBinding"
contract = "IMyContract"
/>
<endpoint
address = "MyService" <!-- net.tcp://localhost:8081/MyService -->
binding = "netTcpBinding"
contract = "IMyContract"
/>
<endpoint
address = "net.tcp://localhost:8002/MyService/"
binding = "netTcpBinding"
contract = "IMyOtherContract"
/>
</service>
我们还可以进一步对端点中的绑定参数进行设置。每种绑定类型可拥有多个名称(name)不同的参数设置,然后在端点的 bindingConfiguration 属性中指定关联设置名称即可。
<system.serviceModel>
<services>
<service name = "MyService">
<endpoint
address = "net.tcp://localhost:8000/MyService/"
bindingConfiguration = "TransactionalTCP"
binding = "netTcpBinding"
contract = "IMyContract"
/>
</service>
</services>
<bindings>
<netTcpBinding>
<binding name = "TransactionalTCP"
transactionFlow = "true"
/>
</netTcpBinding>
</bindings>
</system.serviceModel>
下面是一个完整的演示。
[ServiceContract]
interface IMyService
{
[OperationContract]
void Test(int x);
}
class MyService : IMyService
{
public void Test(int x)
{
Console.WriteLine(x);
}
}
class Program
{
static void Main(string[] args)
{
var host = new ServiceHost(typeof(MyService));
host.Open();
// ------------
var http = ChannelFactory<IMyService>.CreateChannel(new BasicHttpBinding(),
new EndpointAddress("http://localhost:801/MyService"));
using (http as IDisposable)
{
http.Test(1);
}
// ----------------
var tcp = ChannelFactory<IMyService>.CreateChannel(new NetTcpBinding(),
new EndpointAddress("net.tcp://localhost:802/MyService"));
using (tcp as IDisposable)
{
tcp.Test(1);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey(true);
Environment.Exit(0);
}
}
App.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<services>
<service name="Learn.CUI.MyService">
<host>
<baseAddresses>
<add baseAddress="http://localhost:801" />
<add baseAddress="net.tcp://localhost:802" />
</baseAddresses>
</host>
<endpoint
address="MyService"
binding="basicHttpBinding"
contract="Learn.CUI.IMyService"
/>
<endpoint
address="MyService"
binding="netTcpBinding"
contract="Learn.CUI.IMyService"
/>
</service>
</services>
</system.serviceModel>
</configuration>
除了使用配置文件外,我们还可以直接在代码中对端点参数进行设置。
ServiceHost host = new ServiceHost(typeof(MyService));
Binding wsBinding = new WSHttpBinding( );
Binding tcpBinding = new NetTcpBinding( );
host.AddServiceEndpoint(typeof(IMyContract), wsBinding, "http://localhost:8000/MyService");
host.AddServiceEndpoint(typeof(IMyContract), tcpBinding, "net.tcp://localhost:8001/MyService");
host.AddServiceEndpoint(typeof(IMyOtherContract), tcpBinding, "net.tcp://localhost:8002/MyService");
host.Open( );
ServiceHost 类型用来创建 Self-Hosting,AddServiceEndpoint(...) 有多个重载方法,用于添加端点设置。
和上面配置文件中所做的一样,在代码里同样可以使用绝对或相对地址。
Uri baseHttpUri = new Uri("http://localhost:8000");
Uri baseTcpUri = new Uri("net.tcp://localhost:8001");
ServiceHost host = new ServiceHost(typeof(MyService), baseHttpUri, baseTcpUri);
Binding wsBinding = new WSHttpBinding( );
Binding tcpBinding = new NetTcpBinding( );
host.AddServiceEndpoint(typeof(IMyContract), wsBinding, "MyService");
host.AddServiceEndpoint(typeof(IMyContract), tcpBinding, "MyService");
host.AddServiceEndpoint(typeof(IMyOtherContract), tcpBinding, "net.tcp://localhost:8002/MyService");
host.Open( );
至于绑定类型的设置,同样没啥问题。
ServiceHost host = new ServiceHost(typeof(MyService));
NetTcpBinding tcpBinding = new NetTcpBinding( );
tcpBinding.TransactionFlow = true;
host.AddServiceEndpoint(typeof(IMyContract), tcpBinding, "net.tcp://localhost:8000/MyService");
host.Open( );