我们正在尝试定义一个服务结构无状态服务,该服务监听UDP数据。
我们正与微软合作,他们表示支持并且我应该为TCP设置;下面是ServiceManifest.xml文件的片段:
<Resources>
<Endpoints>
<!-- This endpoint is used by the communication listener to obtain the port on which to
listen. Please note that if your service is partitioned, this port is shared with
replicas of different partitions that are placed in your code. -->
<Endpoint Name="ServiceEndpoint" Protocol="tcp" Port="12345" Type="Input" />
</Endpoints>
</Resources>
服务启动正常但我无法使服务接收任何UDP数据,如果我执行netstat -a
,我无法在TCP或UDP中看到任何正在侦听的端口。
我已经在线进行了大量的研究,而且我在创建自定义ICommunicationListener方面没有找到太多,但我希望其他人可以验证是否可以使用SF。
这是ICommunicationListener实现:
public UdpCommunicationListener(string serviceEndPoint,
ServiceInitializationParameters serviceInitializationParameters, Action<UdpReceiveResult> connector)
{
if (serviceInitializationParameters == null)
{
throw new ArgumentNullException(nameof(serviceInitializationParameters));
}
var endPoint = serviceInitializationParameters
.CodePackageActivationContext
.GetEndpoint(serviceEndPoint ?? "ServiceEndPoint");
_connector = connector;
_ipAddress = FabricRuntime.GetNodeContext().IPAddressOrFQDN;
_port = endPoint.Port;
_server = new UdpServer(_ipAddress, _port);
_server.Open();
}
public Task<string> OpenAsync(CancellationToken cancellationToken)
{
_listener = _server.Listen(_connector);
return Task.FromResult($"udp::{_ipAddress}:{_port}");
}
public Task CloseAsync(CancellationToken cancellationToken)
{
this.Abort();
return Task.FromResult(true);
}
public void Abort()
{
_listener.Dispose();
_server?.Close();
}
}
public class UdpServer
{
private readonly UdpClient _udpClient;
private IObservable<UdpReceiveResult> _receiveStream;
public UdpServer(string ipAddress, int port)
{
Id = Guid.NewGuid();
_udpClient = new UdpClient(ipAddress, port);
}
public Guid Id { get; }
public void Open()
{
_receiveStream = _udpClient.ReceiveStream().Publish().RefCount();
}
public void Close()
{
//TODO: Not sure how to stop the process
}
public IDisposable Listen(Action<UdpReceiveResult> process)
{
return _receiveStream.Subscribe(async r =>
{
process(r);
});
}
}
答案 0 :(得分:3)
我有Udp作为无状态服务工作。这是代码:
UdpService.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Fabric;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;
namespace UdpService
{
/// <summary>
/// An instance of this class is created for each service instance by the Service Fabric runtime.
/// </summary>
internal sealed class UdpService : StatelessService
{
private UdpCommunicationListener listener;
public UdpService(StatelessServiceContext context)
: base(context)
{ }
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
yield return new ServiceInstanceListener(initParams =>
{
this.listener = new UdpCommunicationListener();
this.listener.Initialize(initParams.CodePackageActivationContext);
return this.listener;
});
}
}
}
UdpCommunicationListener
using System;
using System.Diagnostics;
using System.Fabric;
using System.Fabric.Description;
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
namespace UdpService
{
public class UdpCommunicationListener : ICommunicationListener, IDisposable
{
private readonly CancellationTokenSource processRequestsCancellation = new CancellationTokenSource();
public int Port { get; set; }
private UdpClient server;
/// <summary>
/// Stops the Server Ungracefully
/// </summary>
public void Abort()
{
this.StopWebServer();
}
/// <summary>
/// Stops the Server Gracefully
/// </summary>
/// <param name="cancellationToken">Cancellation Token</param>
/// <returns>Task for Asynchron usage</returns>
public Task CloseAsync(CancellationToken cancellationToken)
{
this.StopWebServer();
return Task.FromResult(true);
}
/// <summary>
/// Free Resources
/// </summary>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Initializes Configuration
/// </summary>
/// <param name="context">Code Package Activation Context</param>
public void Initialize(ICodePackageActivationContext context)
{
EndpointResourceDescription serviceEndpoint = context.GetEndpoint("ServiceEndpoint");
this.Port = serviceEndpoint.Port;
}
/// <summary>
/// Starts the Server
/// </summary>
/// <param name="cancellationToken">Cancellation Token</param>
/// <returns>Task for Asynchron usage</returns>
public Task<string> OpenAsync(CancellationToken cancellationToken)
{
try
{
this.server = new UdpClient(this.Port);
}
catch (Exception ex)
{
}
ThreadPool.QueueUserWorkItem((state) =>
{
this.MessageHandling(this.processRequestsCancellation.Token);
});
return Task.FromResult("udp://" + FabricRuntime.GetNodeContext().IPAddressOrFQDN + ":" + this.Port);
}
protected void MessageHandling(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, this.Port);
byte[] receivedBytes = this.server.Receive(ref ipEndPoint);
this.server.Send(receivedBytes, receivedBytes.Length, ipEndPoint);
Debug.WriteLine("Received bytes: " + receivedBytes.Length.ToString());
}
}
/// <summary>
/// Receives the specified endpoint.
/// </summary>
/// <param name="endpoint">The endpoint.</param>
/// <returns></returns>
public Task<byte[]> Receive(ref IPEndPoint endpoint)
{
return Task.FromResult(this.server.Receive(ref endpoint));
}
/// <summary>
/// Free Resources and Stop Server
/// </summary>
/// <param name="disposing">Disposing .NET Resources?</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (this.server != null)
{
try
{
this.server.Close();
this.server = null;
}
catch (Exception ex)
{
ServiceEventSource.Current.Message(ex.Message);
}
}
}
}
/// <summary>
/// Stops Server and Free Handles
/// </summary>
private void StopWebServer()
{
this.processRequestsCancellation.Cancel();
this.Dispose();
}
}
}
最后但并非最不重要的是ServiceManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<ServiceManifest Name="UdpServicePkg"
Version="1.0.0"
xmlns="http://schemas.microsoft.com/2011/01/fabric"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ServiceTypes>
<!-- This is the name of your ServiceType.
This name must match the string used in RegisterServiceType call in Program.cs. -->
<StatelessServiceType ServiceTypeName="UdpServiceType" />
</ServiceTypes>
<!-- Code package is your service executable. -->
<CodePackage Name="Code" Version="1.0.0">
<EntryPoint>
<ExeHost>
<Program>UdpService.exe</Program>
</ExeHost>
</EntryPoint>
</CodePackage>
<!-- Config package is the contents of the Config directoy under PackageRoot that contains an
independently-updateable and versioned set of custom configuration settings for your service. -->
<ConfigPackage Name="Config" Version="1.0.0" />
<Resources>
<Endpoints>
<!-- This endpoint is used by the communication listener to obtain the port on which to
listen. Please note that if your service is partitioned, this port is shared with
replicas of different partitions that are placed in your code. -->
<Endpoint Name="ServiceEndpoint" Port="5555" />
</Endpoints>
</Resources>
</ServiceManifest>
答案 1 :(得分:2)
我解决了UdpServer组件的缺陷,现在可以在Service Fabric Service中托管。
代码的问题在于这一行:
_udpClient = new UdpClient(ipAddress, port);
这是侦听流量的错误重载,需要:
_udpClient = new UdpClient(port);
我确实尝试过:
_udpClient = new UdpClient(new IPAddress(IPAddress.Parse(_ipAddress)),port)
但这不起作用;作为检索主机的通信监听(它描述自己)的行返回主机名而不是IP地址,我认为你可以通过对清单进行一些更改来改变这种行为,但现在只需要端口就足够了。