我有一个WCF服务,想使用TCP绑定连接它。这很好,但是你应该如何处理客户端呢?我注意到如果你每次调用都创建一个新的客户端,它不会重用通道并且会留下一堆TCP连接直到它们超时。
创建一个客户端,调用其中的方法,然后关闭它是正常使用吗?
如果你想重新使用连接怎么办?有什么限制吗?你能从不同的线程中同时发起多个调用吗?如果不能,是否必须自己进行连接池管理?在重用连接时,是否需要在发起调用之前检查连接状态并在连接失败时清理它?
我有一个WCF服务,想使用TCP绑定连接它。这很好,但是你应该如何处理客户端呢?我注意到如果你每次调用都创建一个新的客户端,它不会重用通道并且会留下一堆TCP连接直到它们超时。
创建一个客户端,调用其中的方法,然后关闭它是正常使用吗?
如果你想重新使用连接怎么办?有什么限制吗?你能从不同的线程中同时发起多个调用吗?如果不能,是否必须自己进行连接池管理?在重用连接时,是否需要在发起调用之前检查连接状态并在连接失败时清理它?
这里有很多问题需要一起处理,情况有些复杂。创建客户端时,你可以通过服务引用获得从ClientBase<ServiceContract>
派生的类,或者使用ChannelFactory<ServiceContract>
手动创建通道(前一种情况在内部使用了ChannelFactory)。
这与你的问题有何关联?首先让我们看看真正的TCP连接。当你定义NetTcpBinding
时,你可以设置它的MaxConnections
属性(默认值为10)。该属性定义了池化连接的数量。这意味着,如果你向服务器创建客户端通道并关闭通道,则连接不会立即终止。它会保持在池中,直到被同一服务器上另一个打开的客户端通道使用,或者直到其空闲超时到期。你可以打开服务器允许的任意数量的连接,但只有由MaxConnections
定义的数量将在关闭相关的客户端通道时被池化。其他连接将立即终止。如果创建了CustomBinding
,则可以直接使用TCP传输,在那里你也可以控制空闲超时时间(我认为默认值为2分钟)。只要相关的ChannelFactory
未被销毁,连接就会被保留在池中 = 每个应用程序使用一个ChannelFactory
(ClientBase
在内部执行此操作)。
现在让我们谈谈通道本身,因为它与你的其他问题有关。WCF有会话型和非会话型通道。 TcpTransportChannel
是会话型的。这意味着,一旦你打开通道,就创建了一个会话。会话表示单个客户端代理的所有请求默认情况下始终由同一服务实例处理(按会话实例)。但该实例默认为单线程。这意味着,你可以有多个线程使用同一个代理,但服务将按顺序处理请求。如果你想让服务同时处理多个请求,必须使用[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
标记它。一旦你这样做,你就需要负责服务中的线程安全处理(多个线程访问同一个服务实例)。
Sessionful通道有一个很大的缺点。服务上的任何故障或异常都将中断通道,并且通常在您尝试再次使用该通道后才会得知(异常显示通道处于故障状态,无法使用)。您必须始终正确处理这些情况,并且一旦不想使用它们或发生故障时必须正确关闭通道/代理或中止它们。故障通道无法修复-必须中止它并创建新的代理/通道。如果不这样做,连接是否返回池中我不确定。
重用代理/通道取决于您正在构建的应用程序类型。我绝对不会在Web应用程序中的多个请求之间重用代理/通道,但是在WinForm或WPF应用程序中重用绝对可以。
编辑:
是的,ClientBase
在内部使用了ChannelFactory
。 ChannelFactory
的使用方式随时间而变化。 在.NET 3.0中,为每个ClientBase
实例创建了工厂。自.NET 3.5以来,WCF在内部使用MRU高速缓存(最近使用)高速缓存32个最近使用的工厂。要利用此缓存,您必须使用没有参数或带有endpointConfigurationName
和remoteAddress
/ EndpointAddress
的代理构造函数。您不应该在代码中创建端点-这些代理不使用缓存。关于该主题的更多信息请参见此处。
IService service = channelFactory.CreateChannel();
service.DoStuff();
((IContextChannel) service).Close();
这适用于所有的WCF客户端,无论绑定是否为TCP。
更多信息请参见:http://msdn.microsoft.com/en-us/library/aa355056.aspx
SessionMode=NotAllowed
,则您不能使用任何包括 net.tcp 在内的会话通道。您应该始终关闭或中止代理。它实现了IDisposable
接口 - 一旦您看到它,就应该始终处理它。 - Ladislav MrnkaClientBase.Open()
时,这意味着什么?它会从工厂创建一个通道并打开它吗? - RandomEngyClientBase
上调用Open
时,我不确定会发生什么-没有必要调用它,但是该方法可用,因为ClientBase
实现了要求此方法的ICommunicationObject
。 - Ladislav Mrnka