dotnet / MQTTnet

MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker). The implementation is based on the documentation from http://mqtt.org/.
MIT License
4.51k stars 1.07k forks source link

Getting error Only one usage of each socket address #494

Closed vivekspraxa closed 4 years ago

vivekspraxa commented 5 years ago

I am using Startup.cs to start the mqtt server but sometimes getting this error , it will get removed by restating iis

2018-12-12 20:20:16.1372 MQTTServer System.Net.Sockets.SocketException (0x80004005): Only one usage of each socket address (protocol/network address/port) is normally permitted at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress) at System.Net.Sockets.Socket.Bind(EndPoint localEP) at MQTTnet.Implementations.MqttTcpServerListener.Start() at MQTTnet.Implementations.MqttTcpServerAdapter.RegisterListeners(MqttServerTcpEndpointBaseOptions options, X509Certificate2 tlsCertificate) at MQTTnet.Implementations.MqttTcpServerAdapter.StartAsync(IMqttServerOptions options) at MQTTnet.Server.MqttServer.<StartAsync>d__35.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at Color.MQTTServer.CreateServer() in e:\Color\App_Code\Controllers\MQTTServer.cs:line 159 System.Net.Sockets.SocketException (0x80004005): Only one usage of each socket address (protocol/network address/port) is normally permitted at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress) at System.Net.Sockets.Socket.Bind(EndPoint localEP) at MQTTnet.Implementations.MqttTcpServerListener.Start() at MQTTnet.Implementations.MqttTcpServerAdapter.RegisterListeners(MqttServerTcpEndpointBaseOptions options, X509Certificate2 tlsCertificate) at MQTTnet.Implementations.MqttTcpServerAdapter.StartAsync(IMqttServerOptions options) at MQTTnet.Server.MqttServer.<StartAsync>d__35.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at Color.MQTTServer.CreateServer() in e:\Color\App_Code\Controllers\MQTTServer.cs:line 159

chkr1011 commented 5 years ago

What exactly are you executing here. I can see some Controller code and the server. Please provide either a working example or more details on how to reproduce it.

vivekspraxa commented 5 years ago

Here is the code for

MqttServer.cs `using Color.Controllers; using MQTTnet; using MQTTnet.Client; using MQTTnet.Protocol; using MQTTnet.Server; using Newtonsoft.Json; using NLog; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web;

///

/// Summary description for MQTTClient /// /// namespace Color { class WifiMessage { public int Id; public string Message; public string EventTime; } public static class MQTTServer { public static IMqttServer mqttServer = null; private static Logger logger = LogManager.GetLogger("MQTTServer");

    private async static void CreateServer()
    {

        if (mqttServer == null)
        {
        var options = new MqttServerOptions
            {
                ConnectionValidator = c =>
                {

                    c.ReturnCode = MqttConnectReturnCode.ConnectionAccepted;
                }

            };
            mqttServer = new MqttFactory().CreateMqttServer();
            mqttServer.ApplicationMessageReceived += (s, e) =>
            {
                switch (e.ApplicationMessage.Topic)
                {
                    case "server":
                        string json = Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[0]);
                        WifiMessage wm = JsonConvert.DeserializeObject<WifiMessage>(json);

                        break;
                    case "ping":
                        string macAddress = e.ClientId;
                        break;
                    default:
                        logger.Debug(e.ClientId + " reported " + e.ApplicationMessage.Topic + "> " + Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[0]));
                        break;
                }

            };
            mqttServer.ClientDisconnected += (s, e) =>
            {
                string macAddress = e.ClientId;
                logger.Debug("Mqtt Disconnected " + e.ClientId);
            };
            mqttServer.ClientConnected += (s, e) =>
            {
                string macAddress = e.ClientId;
                logger.Debug("Mqtt Connected " + e.ClientId);
            };

            mqttServer.Started += (s, e) => {
            logger.Debug("Mqtt Server started");
        };

            try
            {
                await System.Threading.Tasks.Task.Run(() => mqttServer.StopAsync().GetAwaiter().GetResult());
                await System.Threading.Tasks.Task.Run(() => mqttServer.StartAsync(options).GetAwaiter().GetResult());
            }
            catch (Exception ex)
            {
                logger.Debug(ex);
            }

        }
    }

    public static IMqttServer GetMqttServer()
    {
        if (mqttServer == null)
        {
            logger.Debug("Mqtt GetMqttServer ");
            CreateServer();
        }
        return mqttServer;
    }

    public static void SendMessage(string macAddress, byte[] value, bool retain)
    {
        if (mqttServer == null)
        {
            logger.Debug("Mqtt SendMessage ");
            CreateServer();
        }

        MqttApplicationMessage message = new MqttApplicationMessageBuilder()
            .WithTopic(macAddress)
            .WithPayload(value)
            .WithExactlyOnceQoS()
            .WithRetainFlag(retain)
            .Build();
        mqttServer.PublishAsync(message).GetAwaiter().GetResult();
    }

}

}`

And Startup.cs ` using Microsoft.Owin; using Owin; using Color; using System; using NLog;

[assembly: OwinStartup(typeof(ColoR.Startup))] namespace ColoR { public class Startup { private static Logger logger = LogManager.GetLogger("Startup");

    public void Configuration(IAppBuilder app)
    {
        app.MapSignalR();
        MQTTServer.GetMqttServer();
    }
}

}`

zvrba commented 5 years ago

I just looked up the socket binding code for server and it doesn't set "reuse address" socket option, which it should. Without that option, the port is still "in use" for a couple of minutes after the server has exited.

To the developer: before calling Bind on the socket, you should use https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.setsocketoption?view=netcore-2.2#System_Net_Sockets_Socket_SetSocketOption_System_Net_Sockets_SocketOptionLevel_System_Net_Sockets_SocketOptionName_System_Boolean_ to set SocketOptionName.ReuseAddress to true. (Use SocketOptionLevel.Socket).

EDIT: I have just successfully created the server and connected the client multiple times within a minute w/o any problem. This is on Windows though.

vivekspraxa commented 5 years ago

Is this issue fixed in latest version ?

SeppPenner commented 4 years ago

@vivekspraxa You can fix this by using new MqttServerOptionsBuilder().Build().TlsEndpointOptions.ReuseAddress = true; or anything similar.

See also https://github.com/chkr1011/MQTTnet/issues/872#issuecomment-644053219.