【WCF】终结点的监听地址

作者: 毕衔

更新时间:2022-03-26 13:10:31

3565 阅读

终结点主要作用是向客户端公开一些信息入口,通过这个入口,可以找到要调用的服务操作。通常,终结点会使用三个要素来表述,我记得老蒋(网名:Artech,在园子里可以找到他)在他有关WCF的书里,把这三要素称为“ABC”。

A就是Address,就是终结点的地址;B是Binding,绑定,用于描述传输的协议、是否启用安全模式等;C是Contract,即服务协定。

一个服务协定可以由多个终结点公开,比如一个终结点可能使用HTTP协议,另一个则使用TCP等。

 

WCF是否真的像某些人说的那么复杂难学呢?依老周的低见,是比其他方面的东东稍稍难了些,主要是内容涉及面广,但是,绝不像某些谣言说的那么恐怖。从老周的低劣水平来看,肯定比C++好学(当然了,那不是一回事,C++是练内功的)。编程学习的过程中肯定会遇到难关的,这是必然现象,你不能逃避,你如果想逃避,我建议你还是学学如何被大款们包养吧。

如果你希望你这辈子学有所成,能做些事情,将来起码可以在你的后代面前吹牛的话,不管你学什么,做哪一行,都不可能会轻松的。你要真想不劳而获,想过得轻松,那行,就找个大款包养你吧,这世界上做什么都苦,只有做贱人是最快活的。

提到了C++,自然也包括C,我就想到一个事:常有人问,零基础编程学什么,我可以肯定地回答你,从C开始。不管你将来用不用C,都应该去学,基础不打好,以后你就会付出代价的。

 

好了,P话说多了。关于WCF怎么学,如果你胆子够大,来来来,听听老周的学习方法。其实,当老周遇到难学的东西时,第一件事就是把各种理论和概念全扔一边,并且无视其存在,然后专心于代码,反正先别管它是什么东西,学会了代码怎么写,再回过头去看那些概念啊,理论啊什么的。反正老周不太喜欢概念性的东西,说得玄乎又玄,看都看不懂,还故意装逼,弄得连语义都不通畅,朗读起来还特别的别扭。

那些搞理论的人,似乎喜欢卖弄。老周向来喜欢简单易懂的东西,最好说得连小娃娃都能听得懂,那才叫牛逼,难道不是吗?人家爱因斯坦那么NB的人了,都学着尝试给一老太太讲他的研究成果。

 

唉,又说了一段F话,现在进入主题,今天咱们聊聊终结点的监听地址。

通常,我们在定义终结点的时候,会通过Address属性指定一个地址,客户端通过这个地址可以找到对应的服务操作,这样才能与服务器通信。这好比你让你朋友来你家玩,你总得告诉他你住哪里,只要有地址,哪怕他拿着手机开着GPS也能找到你,不然的话,难道你让他发寻人启事来找你不成?

直接设置终结点的Address属性,你必须指定一个有效的真实地址,如果在服务中指定了基址,那么终结点可以用相对地址,前提是,终结点的地址要与基址的协议相同,比如,都是HTTP。

协议、主机(端口)是必须是真实的,至于后面的路径,可以随便,比如这样:

http://192.168.1.7:8888/worker/sendout/

http://192.168.1.7:8888/feedback/

 

HTTP是协议,要明确;192.xxx.xxx.xxx:8888是主机和端口,要真实,不然客户端找不到。而后面的/.../..是可以随意指定,这些路径只用于标识终结点。

但是,你会注意到,终结点还有一个属性叫ListenUri,这又是啥呢?这个东东是服务器用来监听客户端请求的地址,必须是真实的地址。那么,监听地址和Address又是啥关系呢?

如果只指定了Address,而没有指定ListenUri,那么,Address必须是真实的地址,服务器就会以Address作为监听地址,从此地址接收客户端连接。即Address和ListenUri相同。

如果明确指定了ListenUri,服务就会在此地址上监听客户端连接,Address值既可以用一个真实地址,也可以用一个非真实地址。在这种情况下,Address成了逻辑地址,仅用来标识这个终结点而已。

比如,我设置了ListenUri为:http://DogPC:80/in/,

然后,Address就可以这样http://DogPC:1122/wang_wang/,也可以这样:http://fuck.org。这时候,Address的地址可以是不存在的,不信你上网查查,哪个网站会叫fuck.org。

但是,你得注意了,就算Address成了逻辑地址,它的协议必须与ListenUri的协议相同。如果监听地址是http,那么逻辑地址也要是http,如果监听地址是net.tcp://www.垃圾.com/getcue,你设置逻辑地址为http://mouse.net,这样是不可以的,因为监听地址的协议是net.tcp,所以逻辑地址也必须是net.tcp协议。

 

这么一来,监听地址就成了“中转站”,服务在此处监听连接,一旦有客户端请求,就会接收,然后它不会对收到的消息进行处理,它会把消息调度到专门的通道(Channel)进行处理。说白了就是:监听地址只负责收信,不负责拆信看信和回信

这让老周想起上大学的时候,我们学校有一个专门的点(ListenUri),快递员来了统一到这个点,那里有老师和学生值班,统一收件,然后会打电话给某位同学,或者到某栋宿舍某室通知你,有快件,你去拿吧。

假如,老周当年雄心勃勃地准备去考研,于是和几位同学一起,在网上订了几本复习资料。书到了之后,快递员会拿到那个点,那里有老师专门收件,但老师只负责收件,至于里面的东西如何就不管了。然后老师会通知老周,请到大厅收发处取件。于是老周骑着九年前买的自行车,飞到大厅,取回物件,回到宿舍再拆包。

 

下面,咱们来看个例子,服务协定和服务类的定义就不发了,这些相信大家都懂,咱们看看服务器的终结点配置。

      <service name="sv">
        <endpoint address="http://supperman.org" contract="add" listenUri="http://localhost:6000/input" listenUriMode="Explicit" binding="netHttpBinding"/>
        <endpoint address="http://supperegg.org" contract="sub" binding="netHttpBinding" listenUri="http://localhost:6000/input" />
      </service>

 

supperman.org和supperegg.org都是假的地址,压根儿没这域名,之所以Address可以这么配置,是因为我为两个终结点设置了listenUri,监听的地址必须是真实的。对了,listenUriMode最好使用默认值,即Explicit,这样做是方便客户端配置;如果用的值是Unique,就会在监听地址后面添加一个GUID作为后缀,关键是这个GUID每次运行服务都会重新生成,每次都不同,这不方便客户端配置。当然了,它也是有用的,如果要区分两个地址的话,可以让它唯一,或者你干脆换个端口。

大家可能发现了,两个终结点使用的是相同的监听地址。listenUri是可以多个终结点共用的,因为它只负责收消息,而不处理消息,它只起到转发的作用。但是,address是不能相同的,因为它要用来作为终结点的标识,更何况,前面讲过的ABC,如果两个终结点的ABC都相同,那没什么意义。

 

在客户端,终结点的address、binding、contract也要与服务器匹配。

    <client>
      <endpoint name="epadd" address="http://supperman.org" contract="add" binding="netHttpBinding" behaviorConfiguration="bv"/>
      <endpoint address="http://supperegg.org" contract="sub" binding="netHttpBinding" name="epsub" behaviorConfiguration="bv"/>
    </client>

由于刚才在服务器上,address成了逻辑地址,而不是真实的地址,所以必须配置它的Behavior,加入ClientVia行为,以指定真实的地址。

      <endpointBehaviors>
        <behavior name="bv">
          <clientVia viaUri="http://localhost:6000/input"/>
        </behavior>
      </endpointBehaviors>

ViaUri必须填上服务的监听地址。

 

然后,可以尝试调用一下。

            using (ChannelFactory<CommonContracts.IAddCompute> fac1 = new ChannelFactory<CommonContracts.IAddCompute>("epadd"))
            using (ChannelFactory<CommonContracts.ISubCompute> fac2 = new ChannelFactory<CommonContracts.ISubCompute>("epsub"))
            {
                CommonContracts.IAddCompute channel1 = fac1.CreateChannel();
                CommonContracts.ISubCompute channel2 = fac2.CreateChannel();
                int r1 = channel1.Add(1, 5);
                int r2 = channel2.Sub(9, 6);
                Console.WriteLine($"结果1:{r1}\n结果2:{r2}");

                Console.Read();
            }

 

现在,我们对比一下。

先看客户端发出的请求消息。

 

然后,再看看服务器收到的请求消息。

 

两条消息内容一样,但你细心看,就会发现,服务器收到消息后对消息进行了更改,多加了一个 To 消息头,这个消息头的内容和终结点的逻辑地址(Address)相同,这样就相当于在监听地址收到请求消息,然后转发到终结点对应的通道进行处理。

 

好了,今天的烂文写完了,怎么样,不算难吧。

 

示例源代码下载地址

 

版权声明:本文著作权归作者【毕衔 】所有,不代表本网站立场。

侵权请联系:root_email@163.com

相关推荐