3. 通过编码方式实现基于 WCF 服务的 HelloWorld 实例

项目基本结构

图 1 的 Client 工程对应图 2 的 Client App。
图 1 的 HelloWorldService 工程对应图 2 的 Service,具体的 WCF 服务是由 HelloWorldService 提供的。
图 1 的 Host 工程对应图 2 的 ServiceHost,要为HelloWorldService这个服务来提供一个运行的环境。

图1:项目目录

图2:WCF 架构

1.WCF 服务创建

  • 由 HelloWorldService 工程实现 WCF 服务创建
  • 为了能够使用WCF,在引用里面需要添加 System.ServiceModel 命名空间
  • 接口定义了服务要完成哪些事情,在这个接口中要完成 GetHelloWorld 这个操作并且要有一个字符串类型的返回值
  • 在接口上面多了一些属性 ServiceContractOperationContract,这些属性是在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();
    }
}
  1. 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 工程创建步骤:
    1. 创建主机
    • 在 Main 主函数里,我们创建一个ServiceHost host = new ServiceHost(),也就是创建一个主机,这个 host 所对应的服务实例是 HelloWorldService 工程里面实现的那个 HelloWorldService 类。
    • ServiceHost host = new ServiceHost(typeof(HelloWorldService.HelloWorldService)) 这句代码的意思是我们启动了一个新的主机,这个主机里面现在运行着一个服务,这个服务是由 HelloWorldService.HelloWorldService 这个类来进行实现的。
    1. 添加新的 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

    1. 打开主机
      然后 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 服务

  • 步骤:

    1. 创建 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
    1. 通过代理访问服务
      在创建完 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协议的分布式的调用:

  1. 首先启动 Host 端
  2. 然后启动 Client 端


    运行时效果

总结

  • 服务端:
  1. 定义和实现服务契约:
    在服务端,不但要完成具体服务的业务逻辑,还要完成这个服务相应的契约
  2. 为服务类型创建 ServiceHost 实例,暴露 Endpoint:
    创建 ServiceHost 实例可以理解为我们创建了一个服务,并把这个服务放到服务器里来处理,同时要暴露出 Endpoint 端点,使得客户端来访问 ServiceHost 里面对应的 Service 服务
  3. 打开通信的通道:
    使得客户端能够访问服务端的这些服务
  • 客户端
  1. 需要服务契约的一个副本和关于 Endpoints 端点的信息:
    服务契约的一个副本可以理解为在这个服务当中包含的契约的一个接口,虽然说客户端不需要服务的具体的业务逻辑,但是客户端需要这个服务当中包含契约的一个接口,来告诉客户端它如何与服务端进行通信应该遵循哪些规则,Endpoint端点的信息主要包含了服务器端服务的具体地址以及使用什么样的协议进行具体的通信
  2. 为特定的Endpoints构建通信通道并且执行调用操作已完成我们具体的服务:
    下面这个图三更好的说明了客户端和服务端具体的Endpoint的一个作用

我们就以ABC来进行一下阐述,一个消息要想传送出去或者想要接收到的话必须要有ABC这三样东西的,这三样是缺一不可的:

  • A表示Address地址,这个主机在哪,像上面提到的”net.tcp://localhost:9000/HelloIndigo“,这个就是我们所说的地址,它告诉客户端要去哪里访问主机当中的服务

  • B表示Binding绑定,Binding告诉我们如何来进行通信,像上面提到的我们使用的Tcp的协议来进行通信

  • C表示Contract契约,Contract告诉我们究竟要传什么,要干什么,还对我们要干什么的操作进行了具体的约束,我们做这件事情的名称和参数都是用Contract契约来描述的


    图 3
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351