[英]WCF discovery with service hosts using net.tcp://0.0.0.0:0/blah announces net.tcp://0.0.0.0:0/blah
I wanted a discoverable service that would listen on all interfaces and publish discovery announcements for each interface. 我想要一个可发现的服务,它将监听所有接口并发布每个接口的发现公告。 I was hoping to be able to eventually just configure this in the config file using tcp://0.0.0.0:0/blah as the service endpoint.
我希望最终能够使用tcp://0.0.0.0:0 / blah作为服务端点在配置文件中配置它。 But when I run the code below, the announcements that it sends out use tcp://0.0.0.0:0/blah as the EndpointAddress which is useless to clients.
但是当我运行下面的代码时,它发出的通知使用tcp://0.0.0.0:0 / blah作为对客户端无用的EndpointAddress。
I want to receive announcements for every endpoint it derived from tcp://0.0.0.0:0/blah and I would prefer to use a config file and not a programmatic service host setup like below. 我想收到从tcp派生的每个端点的公告://0.0.0.0:0 / blah我更喜欢使用配置文件而不是下面的程序化服务主机设置。 Any ideas for a workaround?
任何解决方法的想法?
[TestFixtureSetUp]
public void SetUp()
{
service1 = new MyContract();
EndpointDiscoveryBehavior discoveryBehavior = new EndpointDiscoveryBehavior();
ServiceDiscoveryBehavior serviceDiscoveryBehavior = new ServiceDiscoveryBehavior(discoveryUri);
serviceDiscoveryBehavior.AnnouncementEndpoints.Add(new UdpAnnouncementEndpoint(announcementUri));
serviceHost1 = new ServiceHost(service1,
new Uri[] {new Uri("net.pipe://localhost"), new Uri("net.tcp://0.0.0.0:0")});
ServiceEndpoint localEndpoint1 = serviceHost1.AddServiceEndpoint(typeof (IContract),
new NetNamedPipeBinding(),
"/Pipe");
ServiceEndpoint localEndpoint2 = serviceHost1.AddServiceEndpoint(typeof (IContract),
new NetTcpBinding(),
"/Tcp");
localEndpoint2.Behaviors.Add(discoveryBehavior);
serviceHost1.Description.Behaviors.Add(serviceDiscoveryBehavior);
serviceHost1.AddServiceEndpoint(new UdpDiscoveryEndpoint(discoveryUri));
serviceHost1.Open();
}
While my solution may not be "correct", strictly speaking (this should really be fixed in WCF itself, if you ask me), it works, and is sufficient for my purposes. 虽然我的解决方案可能不是“正确的”,严格来说(如果你问我,这应该在WCF本身中确定),它可以工作,并且足以达到我的目的。
First, declare a new endpoint behavior, like so: 首先,声明一个新的端点行为,如下所示:
public class WcfDiscoveryAddressFixEndpointBehavior : IEndpointBehavior, IDispatchMessageInspector
{
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
// Attach ourselves to the MessageInspectors of reply messages
clientRuntime.CallbackDispatchRuntime.MessageInspectors.Add(this);
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
object messageProperty;
if (!OperationContext.Current.IncomingMessageProperties.TryGetValue(RemoteEndpointMessageProperty.Name, out messageProperty)) return null;
var remoteEndpointProperty = messageProperty as RemoteEndpointMessageProperty;
if (remoteEndpointProperty == null) return null;
// Extract message body
string messageBody;
using (var oldMessageStream = new MemoryStream())
{
using (var xw = XmlWriter.Create(oldMessageStream))
{
request.WriteMessage(xw);
xw.Flush();
messageBody = Encoding.UTF8.GetString(oldMessageStream.ToArray());
}
}
// Replace instances of 0.0.0.0 with actual remote endpoint address
messageBody = messageBody.Replace("0.0.0.0", remoteEndpointProperty.Address);
// NOTE: Do not close or dispose of this MemoryStream. It will be used by WCF down the line.
var newMessageStream = new MemoryStream(Encoding.UTF8.GetBytes(messageBody));
XmlDictionaryReader xdr = XmlDictionaryReader.CreateTextReader(newMessageStream, new XmlDictionaryReaderQuotas());
// Create a new message with our modified endpoint address and
// copy over existing properties and headers
Message newMessage = Message.CreateMessage(xdr, int.MaxValue, request.Version);
newMessage.Properties.CopyProperties(request.Properties);
newMessage.Headers.CopyHeadersFrom(request.Headers);
request = newMessage;
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
}
This endpoint behavior replaces the original WCF Discovery reply message with a copy in which instances of 0.0.0.0
have been replaced with the address from which the message was received, available in RemoteEndpointMessageProperty
's Address
property. 此端点行为将原始WCF发现回复消息替换为一个副本,其中
0.0.0.0
实例已替换为接收消息的地址,可在RemoteEndpointMessageProperty
的Address
属性中找到。
To use it, just add the new endpoint behavior to the UdpDiscoveryEndpoint
when you're creating the DiscoveryClient
: 要使用它,只需在创建
DiscoveryClient
时将新端点行为添加到UdpDiscoveryEndpoint
:
var udpDiscoveryEndpoint = new UdpDiscoveryEndpoint();
udpDiscoveryEndpoint.EndpointBehaviors.Add(new WcfDiscoveryAddressFixEndpointBehavior());
_discoveryClient = new DiscoveryClient(udpDiscoveryEndpoint);
// Proceed as usual.
I found another solution that works without altering messages. 我找到了另一种解决方案,无需更改消息。
I noticed that an endpoint has an address
and a listening address
. 我注意到端点有一个
address
和一个listening address
。 The idea is to create an endpoint for each ip address and share only one listening address (0.0.0.0 or use the machine name to get also ipv6). 我们的想法是为每个IP地址创建一个端点,并只共享一个监听地址(0.0.0.0或使用机器名称来获取ipv6)。
On the discovery side, all the address will be received, someone could try to connect to find which one of them is reachable. 在发现方面,将收到所有地址,有人可以尝试连接以查找哪些地址可以到达。
Ther server part looks like this: Ther服务器部件如下所示:
var listenUri = new Uri("net.tcp://<Environment.MachineName>:<port>/IServer";
var binding = new NetTcpBinding(SecurityMode.None);
foreach (ip address)
{
var addr = new Uri("net.tcp://<ip>:<port>/IServer");
Host.AddServiceEndpoint(typeof(IService), binding, addr, listenUri)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.