上次写了一篇《关于Remoting的疑惑》,结果令人满意,解决了一个难题。但今天的问题有些奇怪,归结原因,还是对Remoting的内在机制不甚了解。
问题如下:
Remoting传递远程对象是通过通道来传递的,而每个通道将占用一个端口。要在服务端提供远程对象的实现,首先要Register通道,然后将对象Marshal。如果要停止该远程对象,再通过Disconnect。但该通道仍然存在。如果要停止通道,必须通过Unregister来完成。此时注销了通道,应该释放了对该端口的占用。
如果没有注销通道,又再次注册通道,则会抛出异常“通常每个套接字地址 (协议/网络地址/端口)
只允许使用一次。”因为在Remoting里,要求通道的名字必须是唯一的,且每个通道只能对应一个端口。
在我的服务器端正是按照如上的规则来实现的。运行服务器端程序,注册通道-〉注销通道-〉注册通道,一切OK!为了测试Remoting的性能,再运行客户端程序。在服务器端注册通道并Marshal远程对象的情况下,在客户端程序调用该远程对象,运行正常,该对象被激活并能调用对象的方法。此时注销通道(在服务器端,我的代码在注销通道前,已经将该通道下的所有远程对象Disconnet了),然后再注册通道,问题出现,系统抛出了异常“通常每个套接字地址 (协议/网络地址/端口)只允许使用一次。”
请注意看,在服务器端上的操作是完全一致的:注册通道-〉注销通道-〉注册通道。唯一不同的是在注册通道和注销通道之间,客户端调用了远程对象。
疑问:按道理RemotingServices.UnregisterChannel()方法在注销了通道后,应该释放了对该端口的占用。但从目前的情况来看,当客户端调用了远程对象后,该对象的代理信息保存在通道里,此时即使注销了通道,但由于通道保留了未被销毁的信息,因此端口仍然在占用中。是这样吗?
分析:上述分析显然错误。因为我在注销通道前,已经通过Disconnect()方法断开了该对象和通道的连接,此时远程对象的代理信息在通道中应该不存在了。如果此时通过客户端去调用该对象方法,会抛出异常“未找到指定的服务”,事实正是如此!如果该代理信息仍然存在的话,是不会抛出异常的。那么是对象的生命周期在作怪吗?我将租用管理器的InitialLeaseTime和RenewOnCallTime分别设置为1分钟和20秒,再调用远程对象。然后注销服务器端通道,过两分钟后,再注册,仍然如此。仔细分析,确实不应该是生命周期的原因,因为所谓租用时间到期,即是要Disconnect对象。而我在Unregister时,已经指定停止该对象了。
那么是否因为客户端调用远程对象的缘故,即使注销了通道,由于该服务器对象仍然存在,则客户端与服务器端的连接仍然存在,所以该端口还被占用呢?然而当我注销了通道后,再在客户端调用远程对象时,得到的却是异常,报告我“基础连接已经关闭:无法连接到远程服务器。”这说明这个连接已经不存在了啊。
那么究竟原因何在呢?
补充:首先说明,上面在服务器提供远程对象的方式为Marshal和Disconnect方式。如果我采用RemotingConfiguration的静态方法RegisterWellKnownServiceType()方法以SingleTon模式激活对象。那么又是怎样呢?
先注册通道,然后注册远程对象。这时启动客户端程序,调用远程对象方法,此时服务器会激活该对象,调用成功。然后在服务器端注销该通道后,通过客户端调用远程对象方法。由于注销通道有一定延迟,因此此时调用仍然是成功的。也就是说该对象通过通道与服务器端的连接仍然是存在的!现在再一次注销服务器端通道,可是此时操作是成功的。
这就完全推翻了我前面的猜测。也就是说注销通道后的再注册通道应该与远程对象无关。那么使用Marshal和Disconnet方式,为何会出现这个问题?
唉,头都大了。