[原创]WCF后续之旅(14):TCP端口共享

在基于TCP/IP协议簇的对等网络通信下,相互通信的应用程序运行各自的进程中,出于应用层的进程将数据局封装成数据报,并通过传输层的TCP或者UDP进行网络通信。而TCP和UPD则通过一个16bit的端口来识别不同的应用程序。

对于一些常用网络服务,他们都有一个知名的端口好与之匹配。比如,FTP服务是用的TCP端口为21;Telnet服务的TCP端口为23等等。而对于客户端通常对所使用的端口并不关心,只需要保证端口在本机是唯一的就可以了,这样的端口又成为临时端口,临时端口一般在1024到5000之间。

一般来讲,在某一个时刻,一个端口只能供一个应用程序使用。对于WCF来说,当我们通过一个托管的应用程序对某个服务进行寄宿的时候,一个端口被该应用程序独占使用。如何多个寄宿进行使用相同的端口

在下面的例子中,我通过两个不同的控制台应用程序对两个服务,Service1和Service2进行寄宿,两个服务的终结点地址共享相同的端口:9999

   1: using(ServiceHost serviceHost = new ServiceHost(typeof(Service1)))
   2: {
   3:        serviceHost.AddServiceEndpoint(typeof(IService1), new NetTcpBinding(), "net.tcp://127.0.0.1:9999/service1");
   4:        serviceHost.Open();
   5:        Console.Read();
   6: } 
   1: using(ServiceHost serviceHost = new ServiceHost(typeof(Service2)))
   2: {
   3:        serviceHost.AddServiceEndpoint(typeof(IService2), new NetTcpBinding(), "net.tcp://127.0.0.1:9999/service2");
   4:        serviceHost.Open();
   5:        Console.Read();
   6: } 

当我们先后运行这两个服务寄宿应用程序,第一个能够正常运行,但是对于第二个,则会抛出如下一个AdressAlreadyInUseException异常,错误信息为:

IP 终结点 127.0.0.1:9999 上已有侦听器。请确保未在应用程序中多次尝试使用该终结点,并确保没有其他应用程序在侦听该终结点。

TCP Port Sharing2

在本节中,我们将介绍如何解决这种端口被某一个应用程序独占使用的问题,让不同的监听程序能够共享同一个端口。在这之前,我们需要了解一下,端口的共享具有什么现实的意义。

一、 端口共享在WCF中的意义何在?

在一般的网络环境中,尽可能避免网络攻击,都会通过防火墙将绝大部分的端口封掉,仅仅保留那些常用的网络服务所用的端口,或者为某一个类应用保留少量的端口。总而言之,我们不能保证每个跨防火墙通信的应用都具有一个唯一的端口,他们只能共享一个或者少量的几个端口。

对于Intranet内部,为了保证部署于局域网内的其他计算机的网络应用能够与本机进行正常通信,通常会在本机的防火墙中预留一个可用的端口。Intranet内部的主机之间可以使用这些预留的端口通过相应的传输协议,比如TCP、HTTP、Named Pipe等等,进行通信。而对于处于Internet和本地网络之间的防火墙,通常仅仅只有保留80端口,保证基于HTTP的网络通信能够正常进行。

所以,无论是基于Intranet还是Internet,无论是采用何种传输协议,端口共享——让多个网络应用程序使用相同的端口进行通信,都具有重要的现实意义。

对于WCF来讲,当我们将某个服务寄宿于一个进程中,实际上就是通过该进程监听和处理来自客户端的Socket请求。在一般情况下,一个端口被一个监听进行独占使用,也就是说,如何你的主机上部署了若干服务,而这些服务寄宿于不同的应用程序中,对于这种寄宿应用程序来说,监听的端口必须不同。

所以,我们需要通过特殊的途径实现基于WCF寄宿的端口共享。对于采用不同的传输协议,我们有不同的解决方案,对于HTTP协议,我们可以通过IIS的寄宿方式实现端口的共享,对于TCP,.NET Framework3.0提供了一个特殊的Windows服务,Net.TCP Port Sharing Service,帮助我们轻松的实现端口的共享。我们接下来就讨论这种端口共享解决方案。

二、Net.TCP Port Sharing Service

从功能上讲,Net.TCP Port Sharing Service实现了和HTTP.SYS相同的功能:请求的监听和分发(request listening and dispatching)。唯一不同是,HTTP.SYS运行在内核模式(Kernel Mode)下,而Net.TCP Port Sharing Service运行在用户模式(User Mode)下。

WCF对Net.TCP Port Sharing Service提供了原生的支持。Net.TCP Port Sharing Service在WCF的实现原理如下图所示:在Net.TCP Port Sharing Service开启的状态下,如果我们通过两个服务寄宿应用程序分别寄宿两个服务,Service1和Service2,并且它们共享一样的监听端口:8888。实际上,当ServiceHost的Open方法被执行的时候,WCF会将这两个地址,net.tcp://artech.com:8888/service1和net.tcp://artech.com:8888/service2注册到Net.TCP Port Sharing Service中。而对于Net.TCP Port Sharing Service来说,在其内部维护者一个目的地址和进程的列表,在进行目的地址注册的时候,会将这两个地址和对应的服务寄宿地址的匹配关系添加到该列表之中。

当我们的服务客户端,proxy1和proxy2,分别调用service1和service2。当基于他们各自服务调用的socket连接请求抵达artech.com的时候,Net.TCP Port Sharing Service会截获请求消息,并获取目的地址。根据该目的地址,结合内部维护的目的地址和目标进程匹配列表,Net.TCP Port Sharing Service得到对应的目标应用程序,并将请求消息向真正的目标程序进行转发。

TCP Port Sharing

三 、基于TCP端口共享的编程

由于WCF下基于TCP的端口共享是建立在Net.TCP Port Sharing Service Windows服务上的。所有安装有.NET Framework3.0的操作系统都具有该Windows服务,但是在默认的情况下,该服务是不可用的。当你第一次使用Net.TCP Port Sharing Service,或者发现该服务被禁用,你需要手工的启用该服务。

注:通过“开始”-〉“控制面板”-〉“管理工具”-〉服务,打开如下图所示的“服务对话框”,然后定位到Net.TCP Port Sharing Service。

在基于TCP的WCF通信中,我们使用NetTcpBinding处理通信的所有细节,这些细节中也包括端口的共享。为此在NetTcpBinding中,定义了一个特殊的属性,PortSharingEnabled,表明是否启动端口共享机制。

   1: public class NetTcpBinding : Binding, IBindingRuntimePreferences
   2: {
   3:       // ... ...
   4:       public bool PortSharingEnabled { get; set; }
   5: } 

如何我们以代码的方式进行服务的寄宿的话,我们仅仅需要将终结点的NetTcpBinding上将该属性设为True就可以了:

   1: using(ServiceHost serviceHost = new ServiceHost(typeof(Service1)))
   2: {
   3:       NetTcpBinding binding = new NetTcpBinding();
   4:       binding.PortSharingEnabled = true;
   5:       serviceHost.AddServiceEndpoint(typeof(IService1),binding,"net.tcp://127.0.0.1:9999/service1");
   6:       serviceHost.Open();
   7:       Console.Read();
   8: } 

当然,你也通过通过培植的方式来指定NetTcpBinding的PortSharingEnabled属性:

   1: <configuration>
   2:     <system.serviceModel>
   3:         <bindings>
   4:             <netTcpBinding>
   5:                 <binding name="portSharingBinding" portSharingEnabled="true" />
   6:             </netTcpBinding>
   7:         </bindings>
   8:         <services>
   9:             <service name="Artech.WcfServices.Services.CalculateService">
  10:                 <endpoint binding="netTcpBinding"   bindingConfiguration="portSharingBinding"
  11:                     contract="Artech.WcfServices.Contracts.ICalculate" />
  12:             </service>
  13:         </services>
  14:     </system.serviceModel>
  15: </configuration>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"java.io.IOException: Stream closed"是一个常见的错误消息,它表示在处理输入或输出流时出现了问题。在Java中,输入和输出流是用于读取和写入数据的工具。 通常情况下,抛出这个异常的原因是由于输入或输出流在操作之前已被关闭。当我们使用Java的IO类进行输入或输出操作时,我们需要按照一定的顺序正确关闭流,以避免出现此错误。 在遇到这个错误消息时,我们可以检查以下几个方面: 1. 检查是否正确地打开和关闭了输入或输出流。在使用完流之后,我们应该使用`close()`方法来关闭流。 例如,在读取文件时,我们应该使用以下代码片段: ```java try { FileInputStream file = new FileInputStream("myfile.txt"); // 读取文件的代码逻辑 } catch (IOException e) { e.printStackTrace(); } finally { try { if (file != null) { file.close(); // 关闭流 } } catch (IOException e) { e.printStackTrace(); } } ``` 2. 检查代码中是否存在多个线程尝试共享同一个流。当多个线程同时对同一个流进行操作时,可能会导致其中一个线程关闭了流,而其他线程尝试读取或写入数据时抛出"Stream closed"异常。确保在多线程环境中正确同步流的访问。 3. 检查流对象是否被重复使用。有时我们可能会在多个地方使用相同的流对象进行读写操作。如果在一次操作之后关闭了流,在后续操作中再次使用该流对象将导致"Stream closed"异常。确保每次操作都使用一个新的流对象。 总之,当遇到"java.io.IOException: Stream closed"异常时,我们应该仔细检查流的打开和关闭过程,确保在正确的时间关闭流,并避免多个线程或重复使用流对象造成的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值