Home > Back-end >  NModbus4 TcpListener doesn't start with a valid IP Address
NModbus4 TcpListener doesn't start with a valid IP Address

Time:06-20

Trying to start a TcpListener using NModbus4 library but it won't start with certain IP Addresses. Below is my code:

case "TCP":
try
{
  TcpListener slaveTcpListener = new TcpListener(IP, Port);
  slaveTcpListener.Start();     // Error occurs here
  slave = ModbusTcpSlave.CreateTcp(SlaveID, slaveTcpListener);
  slave.DataStore = DataStoreFactory.CreateDefaultDataStore(MaxCoil, MaxDI, MaxHR, MaxIR);
  slave.DataStore.DataStoreReadFrom  = new EventHandler<DataStoreEventArgs>(dataStore_DataStoreReadFrom);
  slave.ModbusSlaveRequestReceived  = Slave_ModbusSlaveRequestReceived;
  slave.Listen();
}
catch
{
   // Exception Handle
}

The code works fine with IP such as 127.0.0.1 but it won't work with something like 50.50.50.50.

It fails at:

slaveTcpListener.Start();

Even if I validate input IP Address (50.50.50.50 passed the IP validation), the code fails at that line and I don't know how to handle this problem.

CodePudding user response:

As per the docs:

This constructor allows you to specify the local IP address and port number on which to listen for incoming connection attempts.

Now, you don't say what error you got (it was probably "The requested address is not valid in its context") but I'm willing to bet that 50.50.50.50 is not a "local IP address" (that is, an IP Address assigned to the computer the code is running on).

Your computer (or actually its network interfaces) will have IP address(es) assigned to them. TcpListener is providing you the ability to specify which of those addresses to listen on.

127.0.0.1 is the loopback address. If you listen on this address, you will be able to establish local connections only (another computer will not be able to connect to your program over the network).

IPAddress.Any indicates that the server must listen for client activity on all network interfaces. This is what you probably want (i.e. TcpListener slaveTcpListener = new TcpListener(IPAddress.Any, Port);)

Alternatively, you can specify a specific address; but for this to work, the address must be a local IP Address. See this answer for help identifying your local IP addresses.

CodePudding user response:

50.50.50.50 is only a valid IPAddress in that it is syntactically correct and can be parsed without throwing an exception. To start a TcpListener on that IP, the IP must be valid and "pingable" on one of your network interfaces. Most likely, the IP you are trying isn't in one of your HostEntries.

To get a list of valid local host entries, you can enumerate the list returned from System.Net.Dns.GetHostName method:

internal static IEnumerable<IPAddress> GetLocalHostIPs() {
  foreach (var ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList)
    yield return ip;
}

As Brits pointed out, using IPAddress.Any is, in most use cases, what you should use. If you are wanting to only listen on a specific NIC, you should first check that your desired IP is in the HostEntry's address list. One possible way of doing that, without throwing, could be by using a utility function similar to the following:

using System.Net;

/// <summary>
/// Parses an IP string, and verifies that it is in the HostEntry's AddressList.
/// </summary>
/// <param name="localIPString">The string to parse and verify</param>
/// <returns><paramref name="localIPString"/>, if it is a valid local IP and found in the HostName's AddressList; otherwise, <see cref="IPAddress.Any"/></returns>
public static IPAddress TryLocalIPOrAny(string localIPString)
{
    if (localIPString != null && IPAddress.TryParse(localIPString, out IPAddress address))
    {
        if (Dns.GetHostEntry(Dns.GetHostName()).AddressList.Contains(address))
        {
            return address;
        }
    }

    return IPAddress.Any;
}

Then just wrap your IP with that function call when using it to start your listener:

var slaveTcpListener = new TcpListener(TryLocalIPOrAny("50.50.50.50"), 0);
//...
  • Related