SocketException ErrorCode = 10022 when calling Bind on a local endpoint

[title]Problem[/title]

Use a GHI Cobra WiFi and NETMF 4.3, we are trying to bind a new Socket to a local endpoint. The 10022 error code indicates an invalid argument. From the docs:

The argument that we are passing is 192.168.1.202:12000.

[ul]Is our argument invalid? If so, how can we make it valid?
If not, is the state of our socket invalid? If so, how can we make it valid?[/ul]

Here is our source code.

const int internalPort = 12000;
const string internalIPAddress = "192.168.1.202";

var localEndPoint = new IPEndPoint(IPAddress.Parse(internalIPAddress), internalPort);

var serverSocket = new Socket(
    AddressFamily.InterNetwork,
    SocketType.Stream,
    ProtocolType.Tcp);

serverSocket.Bind(localEndPoint);

The code is running on a single board computer with a wireless network adapter. The adapter has already acquired the internalIPAddress that we’re using in the code.

[title]What we have tried[/title]

We have used IPAddress.Any when creating the local endpoint. This Binds successfully but throws at serverSocket.Accept() with ErrorCode = 10050, which indicates that the network is down.

We have tried delaying the bind for three seconds and retrying. This does not help. It still throws.

for (var i = 0; i < maxFailedBindAttempts; ++i)
{
    try
    {
        // Sleep before calling Bind() to prevent 
        // SocketException ErrorCode = 10022
        Thread.Sleep(millisecondSocketBindDelay);
        serverSocket.Bind(localEndPoint);

        // If we're hear the Bind succeeded. Stop looping.
        break;
    }
    catch (Exception ex)
    {
        _logger.Write(ex.ToString());

        if (i == maxFailedBindAttempts)
        {
            // rethrow the exception
            throw;
        }
    }
}

We have also looked on the GHI Forum for other posts containing ErrorCode 10022.

[ul]https://www.ghielectronics.com/community/forum/topic?id=19268 has no responses.
https://www.ghielectronics.com/community/forum/topic?id=18338 suggests that the problem could be a timing issue.[/ul]

From a quick read-through I didn’t spot anything obviously wrong. Can you provide a complete minimal program.cs that demonstrates the failure so that we can see how you are creating the network? Then we can try it on our hardware too.

@ mcalsyn - It is working! Thank you for encouraging the minimal program. :smiley: :clap: The problem was that we were calling Bind() on the Socket before the WiFi was available.

The fix was to put the socket connection here:

NetworkChange.NetworkAvailabilityChanged += (sender, args) =>
{
    ("NetworkAvailabilityChanged. IsAvailable is now " + args.IsAvailable).Dump();

    if (!args.IsAvailable) return;

   // bind here.

};

Here is the hardware stack:

[ul]G120 Fez Cobra II WiFi
Loader version 4.3.4.0
Firmware version 4.3.6.0[/ul]

Here is the full source code.

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using GHI.Networking;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Net.NetworkInformation;

namespace G120.SocketServer
{
    public class Program
    {
        private const int Port = 12000;
        private const string IpAddress = "192.168.1.115";
        private const string SubnetMask = "255.255.255.0";
        private const string GatewayAddress = "192.168.1.1";
        private const string Bigfont = "BigFont";
        private const string Nutbutter3 = "NutButter3";

        public static void Main()
        {
            var wiFiRs9110 = new WiFiRS9110(
                SPI.SPI_module.SPI2,
                GHI.Pins.G120.P1_10,
                GHI.Pins.G120.P2_11,
                GHI.Pins.G120.P1_9, 4000);

            wiFiRs9110.Open(); // what does this do?

            wiFiRs9110.EnableStaticIP(IpAddress, SubnetMask, GatewayAddress);
            
            ConnectWiFi(wiFiRs9110, Bigfont, Nutbutter3);

            NetworkChange.NetworkAvailabilityChanged += (sender, args) =>
            {
                ("NetworkAvailabilityChanged. IsAvailable is now " + args.IsAvailable).Dump();

                if (!args.IsAvailable) return;

                var threadStart = new ThreadStart(() => ConnectSocket(IpAddress, Port));
                var thread = new Thread(threadStart);
                thread.Start();
            };

            try
            {
                var threadStart = new ThreadStart(() => ConnectSocket(IpAddress, Port));
                var thread = new Thread(threadStart);
                thread.Start();
            }
            catch (Exception)
            {
                ("We expect this to throw because the WiFi is not yet connected.").Dump();
            }

            // wait
            var i = 0;
            while (true)
            {
                Thread.Sleep(5000);
                ("Waiting count " + i++).Dump();
             
                wiFiRs9110.Dump();
            }
        }

        private static void ConnectWiFi(WiFiRS9110 wifiRs9110, string ssid, string password)
        {
            const int millisecondsTimeout = 1000;

            var targetGateway = new WiFiRS9110.NetworkParameters[0];

            while (targetGateway.Length == 0)
            {
                targetGateway = wifiRs9110.Scan(ssid);
                Thread.Sleep(millisecondsTimeout);
            }

            wifiRs9110.Join(ssid, password);
        }

        private static void ConnectSocket(string ipAddress, int port)
        {
            const int socketBacklog = 25;

            var localEndPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);

            var serverSocket = new Socket(
                AddressFamily.InterNetwork,
                SocketType.Stream,
                ProtocolType.Tcp);

            serverSocket.Bind(localEndPoint);

            serverSocket.Listen(socketBacklog);

            ("#####").Dump();
            ("#####").Dump();
            ("#####").Dump();

            ("Socket connection succeeded!").Dump();
            ("Navigate to " + ipAddress + ":" + port + " in your web browser.").Dump();

            ("#####").Dump();
            ("#####").Dump();
            ("#####").Dump();

            while (true)
            {
                // Wait for an incoming connection; queue it when it arrives.
                // Accept blocks until a connection arrives.
                // See also bit.ly/1S54gul
                using (var clientSocket = serverSocket.Accept())
                {
                    clientSocket.Okay("It works.");
                }
            }
        }
    }

    public static class Extensions
    {
        public static void Dump(this NetworkInterface networkInterface)
        {
            var builder = new StringBuilder();

            builder.AppendLine("NetworkInterface");

            foreach (var dns in networkInterface.DnsAddresses)
            {
                builder.AppendLine("DnsAddress: " + dns);
            }
            builder.AppendLine("GatewayAddress:" + networkInterface.GatewayAddress);
            builder.AppendLine("IPAddress:" + networkInterface.IPAddress);
            builder.AppendLine("IsDhcpEnabled:" + networkInterface.IsDhcpEnabled);
            builder.AppendLine("IsDynamicDnsEnabled:" + networkInterface.IsDynamicDnsEnabled);
            builder.AppendLine("NetworkInterfaceType:" + networkInterface.NetworkInterfaceType);
            builder.AppendLine("PhysicalAddress:" + ByteArrayToHex(networkInterface.PhysicalAddress, ":"));
            builder.AppendLine("SubnetMask:" + networkInterface.SubnetMask);

            builder.ToString().Dump();
        }

        public static void Dump(this WiFiRS9110 wiFiRs9110)
        {
            var builder = new StringBuilder();

            builder.AppendLine("WiFiRS9110");

            wiFiRs9110.NetworkInterface.Dump();

            builder.AppendLine("LinkConnected:" + wiFiRs9110.LinkConnected);
            builder.AppendLine("ActiveNetwork:" + wiFiRs9110.ActiveNetwork);
            builder.AppendLine("NetworkIsAvailable:" + wiFiRs9110.NetworkIsAvailable);
            builder.AppendLine("Opened:" + wiFiRs9110.Opened);

            builder.ToString().Dump();
        }

        public static void Dump(this string output)
        {
            Debug.Print(output);
        }

        public static string ByteArrayToHex(this byte[] byteArray, string separator)
        {
            // even though it is in the NETMF System namespace,
            // BitConverter.ToString(byteArray) does NOT work on Cobra boards.

            var macBuilder = new StringBuilder(byteArray.Length * 2);
            for (var i = 0; i < byteArray.Length; ++i)
            {
                var hex = byteArray[i].ToString("X2");
                if (i != 0)
                {
                    macBuilder.Append(separator);
                }
                macBuilder.Append(hex);
            }

            return macBuilder.ToString();
        }

        public static void Okay(this Socket clientSocket, string message)
        {
            const string crlf = "\r\n";

            var builder = new StringBuilder();

            builder.Append("HTTP/1.1 200 OK");
            builder.Append(crlf);
            builder.Append("Connection: close");
            builder.Append(crlf);
            builder.Append(crlf);
            builder.Append(message);
            builder.Append(crlf);
            builder.Append(crlf);

            var bytes = Encoding.UTF8.GetBytes(builder.ToString());

            clientSocket.Send(bytes);
        }
    }
}
1 Like

I will try to run this code in the next 12-24 hours, but first thing I notice (and should have noticed before) is that you are binding to a specific IP address. Try IPAddress.Any instead of the specific address. Let me know if that works for you. I’ll dig out a matching hardware setup unless I hear back from you that you’ve had success.

1 Like

@ mcalsyn - Why is it important not to bind to a specific IP Address?

On most Berkeley-style socket stacks (Unix, Linux, Windows), using a specific address for bind() will cause your socket to bind ONLY on the interface that hosts that IP address. This is important if you machine has multiple IP-capable interfaces (for example, wireless, ethernet and maybe bluetooth all at the same time, or multiple ethernet cards). Most non-trivial OSs have multiple interfaces, so specifying an address means that you only listen on one of them.

Now on netmf (an lwip-based stack) a lot of that fancy code is left out. My theory is that it just can’t handle address-specific bindings. It will take you far less code to try out than it took me to type this :slight_smile: Give it a shot - I think it may fix your issue, and it saves you some double-bookeeping in your code.

1 Like

@ mcalsyn - Yo. Thank you for the detailed explanation. I appreciate that. Fact is, the code sample that I posted was working already and it also works with IPAddress.Any as you suggested. Sorry that it wasn’t clear that the code I posted worked. Thank you again for your help.

1 Like

Cool - yes, I missed that little detail where you said it was working now :slight_smile: Glad to be of some small help there.

1 Like