简体   繁体   中英

Service Fabric Stateless Server Custom UDP Listener

We are trying to define a Service Fabric Stateless Service which listeners for UDP data.

We are working with Microsoft who have said that it IS supported and that I should set up for TCP; below is the snippet from the ServiceManifest.xml file :

<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>

The service starts fine but I cannot make the service receive any UDP data and if I do a netstat -a I cannot see anything listening on the port in either TCP or UDP.

I've done plenty of research on online and I've not found much on creating custom ICommunicationListener but I'm hoping that someone else might be able to verify if this should be possible with SF.

Here is the ICommunicationListener implementation:

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);
        });
    }
}

I have Udp Working as a stateless service. This is the Code:

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();
        }
    }
}

Last but not least the 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>

There was an defect with the UdpServer component which I resolved and this now works hosted in a Service Fabric Service.

The issue with the code wa with this line :

_udpClient = new UdpClient(ipAddress, port);

This is the wrong overload for listening for traffic, it needed to be:

_udpClient = new UdpClient(port);

I did try :

_udpClient = new UdpClient(new IPAddress(IPAddress.Parse(_ipAddress)),port)

But this doesn't work; as the line from the Communication listen (as it describes itself) which retrieves the host returns the hostname and not the IPAddress, I think you could alter this behaviour by makes some changes to the manifest but just the port was sufficient for now.

Since only Protocol http/https and tcp are supported. I guess you can't do a udp protocol kinda thing. Udp isn't reliable. We were able to use SignalR, but Udp doesn't work I guess.

Edit: You can see in my other Post that Udp is working now.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM