IP and Ping

Using the FEZ Panda II with Ethernet Connect shield and cable connecting the Connect shield to my PC, I’m trying to do two things: A) make Debug.Print show the assigned IP address, and B) ping a network device, such as the PC.

Attempting to do the ping test first, tried the following (borrowed from andre.marschalek), but get an unexplained system error on line “Socket mySocket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);”. Maybe, there’s a better way to do a simple ping test? Here’s my code:

using System;
using System.Net;
using System.Net.Sockets;
using Microsoft.SPOT;
using Socket = System.Net.Sockets.Socket;

namespace MFConsoleApplication8
{
    public class Program
    {
        public static void Main()
        {
            if (Ping("192.168.0.1"))
                Debug.Print("success");
            else
                Debug.Print("failed");
        }

        private static byte lastSequenceNr = 0;
        private static byte[] pingCommand;

        public static bool Ping(string address)
        {
            bool result = false;
            byte[] receiveBuffer = new byte[540];
            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 0);
            IPEndPoint responseEndPoint = new IPEndPoint(IPAddress.Any, 0);
            EndPoint castResponseEndPoint = (EndPoint)responseEndPoint;
            pingCommand = new byte[8];
            pingCommand[0] = 8; // Type
            pingCommand[1] = 0; // Subtype
            pingCommand[2] = 0; // Checksum
            pingCommand[3] = 0;
            pingCommand[4] = 1; // Identifier
            pingCommand[5] = 0;
            pingCommand[6] = 0; // Sequence number
            pingCommand[7] = 0;
            Socket mySocket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);

            mySocket.Bind(localEndPoint);
            mySocket.SetSocketOption(
                SocketOptionLevel.IP,
                SocketOptionName.IpTimeToLive,
                128
                );
            mySocket.ReceiveTimeout = 10;
            DateTime timeSend = DateTime.Now;
            Debug.Print(timeSend.ToString());
            pingCommand[6] = lastSequenceNr++;
            SetChecksum(pingCommand);
            mySocket.SendTo(pingCommand, pingCommand.Length, SocketFlags.None, new IPEndPoint(IPAddress.Parse(address), 0));
            Debug.Print("sended");
            try
            {
                int Brecieved = mySocket.ReceiveFrom(
                    receiveBuffer,
                    0,
                    receiveBuffer.Length,
                    SocketFlags.None,
                    ref castResponseEndPoint
                    );
                if ((receiveBuffer[20] == 0) && (pingCommand[4] == receiveBuffer[24]) && (pingCommand[5] == receiveBuffer[25]) &&
                        (pingCommand[6] == receiveBuffer[26]) && (pingCommand[7] == receiveBuffer[27]))
                {
                    Debug.Print("response received");
                    Debug.Print(DateTime.Now.Subtract(timeSend).ToString());
                    result = true;
                }
            }
            catch (SocketException e)
            {
                if (e.ErrorCode != (int)SocketError.TimedOut)
                {
                    Debug.Print("unexpected error: " + e.ErrorCode);
                }
            }
            mySocket.Close();
            return result;
        }

        private static void SetChecksum(byte[] tel)
        {
            tel[2] = 0;
            tel[3] = 0;
            uint cs = 0;

            for (int i = 0; i < pingCommand.Length; i = i + 2)
                cs += (UInt16)(pingCommand[0 + i] << 0 | pingCommand[1 + i] << 8);

            cs = ~((cs & 0xffffu) + (cs >> 16));
            tel[2] = (byte)cs;
            tel[3] = (byte)(cs >> 8);
        }

        public static UInt16 ToUInt16(byte[] value, int index = 0) { return (UInt16)(value[0 + index] << 0 | value[1 + index] << 8); }


    }
}

I believe the example you used is for an EMX based board. The Panda uses a different processor. The connect shield uses the GHI W5100 library for networking.

http://www.tinyclr.com/codeshare/entry/358 is a great example that shows you everything you could need on Wiz5100.

The connect shield library does not support ICMP sockets.

Thanks for example 358. I reviewed it and tried it. It looks like it might be useful for setting a static IP, but I don’t see an example of how to get the FEZ to ping another device on the network or how to display the IP already assigned to it from my PC (it’s plugged, via ethernet, into my PC). Is there some code in 358 that has these functions?

So lets talk about what you’re trying to achieve?

As Mike says, the example doesn’t show ICMP pinging to another device. The example is best used to prove that you have networking established. More than that is time to hit the API reference, http://www.ghielectronics.com/downloads/NETMF/WIZnet%20Library%20Documentation/Index.html and find out what you can do, how.

Okay, I’ll explain more what I’m trying to achieve. For the initial step, I want the FEZ to ping the controlling PC to know that connectivity has not been interrupted and to keep the motors running. If the ping fails, that means connectivity has dropped and, thus, kill the motors. I just figured that Ping was the best method to accomplish that, but maybe there’s a better way to do the same thing. There is the Poll function, I think.

You should edit your profile so we know who we’re talking to :slight_smile:

“motors running” and connectivity to a PC, maybe you better tell us how the entire “system” hangs together.

If you’re communicating between the two devices, why not simply use sockets and transmit a regular heartbeat over it ? If the heartbeat fails, go to failsafe.

Good idea - at least to make the reminder banner go away. :slight_smile: The “heartbeat” you mentioned is what I’m trying to figure out - how to send a “poll” or “ping” to let the device know when the network connectivity drops.

@ Wayne - If you are already using TCP, you can have a special message that is sent every n seconds which notifies the other end that you are still alive and kicking. If the application misses one or more of these heart beat messages then it can assume the end point is down. Of course this could be two way, the receiving end can send a response the say yes, I see you are alive and i am also alive. This can be optimized when using TCP by only having to send the heart beat message if the system has been idle and there has been no messages flowing.

As an alternative, if you do not want to interfere with your primary TCP stream, you can use UDP to send the heart beat. Every few seconds you send a UDP message to the PC and it sends a response UDP message. This will notify you the the other end is also up and receiving your messages.

The advantage of either of the above over ICMP ping, in my opinion, is that this will actually tell you that your application is active and working. While ICMP Ping will only tell you that the machine is active and responding to ping messages, which does not mean that your application is actually functioning at the time.

Thanks for the advice on the TCP notification. I think your suggestion is sound - a regular notification via this TCP method would suffice in place of ping, and, in fact, be better than ping. So…

I pieced together some code gathered from online here that almost works. The code below enables the device to receive data from a PC on the network. Something strange happens with the .Receive() command, though: the “socket accepted” prints when the first chunk of data is sent to the Panda, but then it seems to drop the connection, after the initial data chunk, right at the .Receive command and won’t process any more data sent to it (my sending application says the connection has been disconnected). The weird part is that if I leave the Panda running, shutdown the sending PC application and restart it, the Panda then receives the data each time it’s sent. Even weirder, at some point after the restart and a few minutes of sitting idle, Panda seems to start running spontaneously and endlessly receiving some empty byte array, that isn’t being sent. (I saw some posts about the .Receive command being updated at some point…I’m running v4.1.8)

[

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Reflection;
//using System.Net;
//using System.Net.Sockets;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Net;
using GHIElectronics.NETMF.Net.NetworkInformation;
using Rhino.Networking;
using GHIElectronics.NETMF.Native;
using GHIElectronics.NETMF.Net.Sockets;

namespace Rhino.Networking
{
    public class Program2
    {
        public static void Main()
        {
            byte[] mac = { 0x00, 0x26, 0x1C, 0x7B, 0x29, 0xE8 }; 
            WIZnet_W5100.Enable(SPI.SPI_module.SPI1, (Cpu.Pin)FEZ_Pin.Digital.Di10, (Cpu.Pin)FEZ_Pin.Digital.Di7, true);
            Dhcp.EnableDhcp(mac, "panda");
            
            Debug.Print("Network settings:");
            Debug.Print("IP Address: " + new IPAddress(NetworkInterface.IPAddress).ToString());
            Debug.Print("Subnet Mask: " + new IPAddress(NetworkInterface.SubnetMask).ToString());
            Debug.Print("Default Getway: " + new IPAddress(NetworkInterface.GatewayAddress).ToString());
            Debug.Print("DNS Server: " + new IPAddress(NetworkInterface.DnsServer).ToString());

            Socket socket1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            EndPoint ep1 = new IPEndPoint(IPAddress.Any, 80);

            socket1.Bind(ep1);
            socket1.Listen(1);

            socket1.Accept();
            Debug.Print("socket accepted");

            while (true)
            {

                byte[] bytes = new byte[1024];
                char[] letters = new char[1024];
                int bytesRec = 0;
                bytesRec = socket1.Receive(bytes);
                
                Debug.Print("Bytes available: " + bytesRec + " from " + socket1.RemoteEndPoint);

                Encoding enc = new UTF8Encoding();
                Decoder decoder = enc.GetDecoder();
                int x, y;
                bool z;
                string data = null;
                decoder.Convert(bytes, 0, bytesRec, letters, 0, bytesRec, true, out x, out y, out z);
                for (int i = 0; i < (letters.Length - 1); i++)
                { data += "" + letters[i]; }
                Debug.Print("Message: " + data);

            }

            Debug.Print("Program ended");
        }
    }
}

@ Wayne - I can’t take a detailed look now, as I am on my way in to the office. But a few things I can point out, the array allocations in the while loop should be moved out of the loop and the you should also check bytesRec for 0 before processing the data.

You seem to be assuming that a single read will receive all the data, I base this on the fact that your for loop is to the length of the array, you cannot assume that the entire message is received in one read. You will need to read data into the buffer until you see you have a complete message and then process the message. Note that at the point that you have a complete message the buffer might also contain the start of another message. With streaming protocols message framing is extremely important,

Sorry I do not have more time right now, if you have not come right I will try get some time this evening to put together a sample.

@ taylorza: Thanks, I’m going to make those changes - they’re good points. I realized I should have included a copy of the PC program sending the data, too, so here it is (it’s in vb.net):

Imports System.IO.Ports
Imports System.Threading
Imports System.Reflection
Imports System.Net.Sockets
Imports System.Text

Public Class Form1

    Public myCounter As Integer
    Public mytcpClient As System.Net.Sockets.TcpClient
    Public networkStream As NetworkStream
    Public AsyncResult As IAsyncResult
    Public AsyncSuccess As Boolean
    Public portNumber As Integer
    Public remoteAddr As System.Net.IPAddress
    Public sendBytes As [Byte]()
    Public TimeoutObject As New ManualResetEvent(False)
    Public IsConnectionSuccessful As Boolean = False
    Public socketexception As Exception

    Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        mytcpClient.Close()
        mytcpClient = Nothing
    End Sub

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

        TextBox1.Text = "Test Message " + myCounter.ToString

        mytcpClient = New System.Net.Sockets.TcpClient
        AsyncSuccess = False
        portNumber = 80
        remoteAddr = System.Net.IPAddress.Parse("192.168.1.4")

        AsyncResult = mytcpClient.BeginConnect(remoteAddr, portNumber, New AsyncCallback(AddressOf nullCallback), mytcpClient)
        AsyncSuccess = AsyncResult.AsyncWaitHandle.WaitOne(1500, True)

    End Sub

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click

        Select Case AsyncSuccess
            Case True
                If mytcpClient.Connected = True Then
                    networkStream = mytcpClient.GetStream()
                    If networkStream.CanWrite And networkStream.CanRead Then
                        'send handshake
                        'sendBytes = Encoding.ASCII.GetBytes(TextBox1.Text)
                        sendBytes = Encoding.UTF8.GetBytes(TextBox1.Text)

                        networkStream.Write(sendBytes, 0, sendBytes.Length)
                    Else
                        Throw New SocketException(10061)
                    End If
                Else
                    Throw New SocketException(10061)
                End If
            Case False
                Throw New SocketException(10061)
        End Select

        myCounter += 1
        TextBox1.Text = "Test Message " + myCounter.ToString

    End Sub

    Sub nullCallback(ByVal ar As IAsyncResult)
      
    End Sub

End Class

@ Wayne - Sorry for the delay, it has been a long day.

Before I proceed, the code I am presenting has only been run once so it should not be considered robust, bug free or anything. It should hopefully serve its purpose, which is to demonstrate the principal.

First let me give you a simplified version of your VB client.


Imports System.Net
Imports System.Net.Sockets
Imports System.IO
Imports System.Text

Public Class Form1
  Private _client As New TcpClient
  Private _stream As NetworkStream

  Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    _client.BeginConnect("192.168.0.101", 80, AddressOf Client_Connect, Nothing)
  End Sub

  Private Sub Client_Connect(ar As IAsyncResult)
    Try
    _client.EndConnect(ar)
      Me.BeginInvoke(Sub()
                       _stream = _client.GetStream()
                       Button1.Enabled = True
                     End Sub)
    Catch ex As Exception
      System.Diagnostics.Debug.WriteLine(ex.ToString())
    End Try
  End Sub

  Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click    
    Dim bytes() As Byte = Encoding.UTF8.GetBytes(TextBox1.Text)
    _stream.WriteByte(CType(bytes.Length, Byte))
    _stream.Write(bytes, 0, bytes.Length)
  End Sub
End Class

I have made a few simplifications, not that I am not doing much error handling, I will leave that to you. I just want the example to be as clear as possible. The key changes here,
I have the button default disabled. Once the connection is established I setup the stream for the client connection and enable the button.

The next thing is in the button click handler you will notice that I am not just sending the message, I am first sending the length of the message, to keep things as simple as possible, I have limited the length to a byte (255). Then the length byte is followed by the bytes of the message. Because TCP is a streaming protocol, you need to take care of framing your messages, in this example I am using a simple message length prefix to frame the message.

The server code as you will see shortly will read the first byte and prepare to receive the number of subsequent bytes as indicated by this first byte. To do this I have written the code as a mini state machine, where there are two states, ReadLength and ReadMessage. Initially the processing of the message is in the ReadLength state, once a single byte has been read and the length determined, the processing then changes to the ReadMessage state, in this state the processing will read the indicated number of bytes. Once the indicated number of bytes has been received we have a complete message, the message is then processed (written to the debug window) and the state switched back to ReadLength in preparation for the next messsage.

Unfortunately I do not have the same network kit as you, I only have a Gadgeteer spider board. But other than the initialization I would expect everything to be very much the same. Note that I have a little helper class that I wrote for this example, it is not very well tested as I just wrote it about 10 min ago, but I will finish it up, test properly and post it as a code snippet soon. So for now just take it for what it is worth.

Here is the entry point.


public partial class Program
  {
    // This method is run when the mainboard is powered up or reset.   
    void ProgramStarted()
    {
      // Setup the network
      ethernet.UseStaticIP("192.168.0.101", "255.255.255.0", "192.168.0.1");

      // Start a new thread to listen for incomming connections
      // I believe you can do exactly this in your main function
      new Thread(() =>
      {
        // This code is running is a separate thread.
        Socket socket1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        EndPoint ep1 = new IPEndPoint(IPAddress.Any, 80);
        socket1.Bind(ep1);
        socket1.Listen(5);
        while (true)
        {
          // Wait for a client to connect
          Socket client = socket1.Accept();

          // Client has connected, we hand it off
          // to another thread to handle the client
          // communication.
          ClientConnection.Process(client);
        }
      }).Start();

      Debug.Print("Program Started");
    }    
  }

This code will sit in a loop accepting client connections, when a client connects we hand the connection off to a new thread to process the client communication and allow the main listening thread to loop around and wait for any new connections from other clients.

The next piece of code is the code that handles the client connection. This code essentially sits in a loop reading the data from the client connection. Initially the state is as described earlier, in ReadLength state. Once we have enough data in the buffer to determine the length (I used only one byte to keep this simple) we can read the length and change state to ReadMessage. In the ReadMessage state we will read data until the full message has been read and then change back to the ReadLength state in preparation for the next message.

Note that because of the nature of the stream, once we have read a message, there might actually already be another message or part of a message in the buffer, therefore if we still have enough data to at least read the length (ie. while _buffer.Count >0) then we loop around and see if we can start processing the next message, if there is not enough data we will just go back and wait on the socket for more data to come.


class ClientConnection
  {
    private const int BufferSize = 1024;

    private Socket _client;
    private byte[] _bytes = new byte[BufferSize];
    private ByteBuffer _buffer = new ByteBuffer();

    public static void Process(Socket client)
    {
      ClientConnection connection = new ClientConnection(client);
      connection.Start();
    }

    private ClientConnection(Socket client)
    {
      _client = client;
    }

    private void Start()
    {
      new Thread(ProcessData).Start();
    }

    private void ProcessData()
    {
      State state = State.ReadLength;
      byte messageLength = 0;
      
      while (true)
      {
        int bytesReceived = _client.Receive(_bytes);
        for (int i = 0; i < bytesReceived; i++)
        {
          _buffer.Enqueue(_bytes[i]);
        }

        while (_buffer.Count > 0)
        {
          switch (state)
          {
            case State.ReadLength:
              if (_buffer.Count >= 1)
              {
                messageLength = _buffer.Dequeue();
                state = State.ReadMessage;
              }
              break;

            case State.ReadMessage:
              if (_buffer.Count >= messageLength)
              {
                byte[] messageBytes = new byte[messageLength];
                _buffer.Dequeue(messageBytes);
                Debug.Print(new string(Encoding.UTF8.GetChars(messageBytes)));
                state = State.ReadLength;
              }
              break;
          }
        }
      }
    }

    enum State
    {
      ReadLength,
      ReadMessage
    }
  } 

Here is the ByteBuffer class, this is essentially a queue that is typed for bytes. This is a very efficient way to handle messages that come in piece by piece. I will flesh this out to be able to pull Int16, Int32, Single, Double etc. which will make this very useful for any kind of TCP or even serial comms.

NOTE: This is not well tested!!!


  public class ByteBuffer
  {
    private byte[] _buffer;
    private int _head;
    private int _tail;
    private int _count;
    private int _capacity;

    private const int _defaultCapacity = 4;

    public ByteBuffer()
    {      
      _buffer = new byte[_defaultCapacity];
      _capacity = _defaultCapacity;
      _head = 0;
      _tail = 0;
      _count = 0;
    }

    public int Count
    {
      get { return _count; }
    }

    public void Clear()
    {
      _count = 0;
      _tail = _head;
    }

    public void Enqueue(byte value)
    {
      if (_count == _capacity)
      {
        Grow();
      }
      _buffer[_head] = value;
      _head = (_head + 1) % _capacity;
      _count++;      
    }

    public byte Dequeue()
    {
      if (_count == 0) throw new InvalidOperationException("Queue is empty");
      byte value = _buffer[_tail];
      _tail = (_tail + 1) % _capacity;
      _count--;      
      return value;
    }

    public int Dequeue(byte[] bytes)
    {
      int maxBytes = System.Math.Min(_count, bytes.Length);
      for (int i = 0; i < maxBytes; i++)
      {
        bytes[i] = Dequeue();
      }
      return maxBytes;
    }

    private void Grow()
    {
      int newCapacity = _capacity << 1;
      byte[] newBuffer = new byte[newCapacity];

      if (_tail < _head)
      {
        Array.Copy(_buffer, _tail, newBuffer, 0, _count);
      }
      else
      {
        Array.Copy(_buffer, _tail, newBuffer, 0, _capacity - _tail);
        Array.Copy(_buffer, 0, newBuffer, _capacity - _tail, _head);
      }
            
      _buffer = newBuffer;
      _head = _count;
      _tail = 0;
      _capacity = newCapacity;      
    }
  }

@ taylorza: Ok, let me just say - 'nuff respect, mon. Your code works beautifully - looking forward to any updates you make (personal wishlist: example of the Panda sending out a heartbeat back to the PC). I had to make some minor changes (mainly to setup the ethernet), but here’s the final code for both the receiver (Panda) and the sender (vb.net program):

Sender:

Imports System.Net
Imports System.Net.Sockets
Imports System.IO
Imports System.Text

Public Class Form1
    Private _client As New TcpClient
    Private _stream As NetworkStream
    Public myCounter As Integer

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        TextBox1.Text = "Test Message " + myCounter.ToString
        _client.BeginConnect("192.168.1.4", 80, AddressOf Client_Connect, Nothing)
    End Sub

    Private Sub Client_Connect(ar As IAsyncResult)
        Try
            _client.EndConnect(ar)
            Me.BeginInvoke(Sub()
                               _stream = _client.GetStream()
                               Button1.Enabled = True
                           End Sub)
        Catch ex As Exception
            System.Diagnostics.Debug.WriteLine(ex.ToString())
        End Try
    End Sub

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim bytes() As Byte = Encoding.UTF8.GetBytes(TextBox1.Text)
        _stream.WriteByte(CType(bytes.Length, Byte))
        _stream.Write(bytes, 0, bytes.Length)
        myCounter += 1
        TextBox1.Text = "Test Message " + myCounter.ToString
    End Sub

End Class

Receiver:

NOTE: For some reason, the compiler didn’t like this line: “Microsoft.SPOT.Net.NetworkInformation.NetworkInterface NI = Microsoft.SPOT.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0];”, so I went back to DHCP. But, I would like to get EnableStaticIP working at some point, just not sure what was wrong with it.

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Reflection;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Net;
using GHIElectronics.NETMF.Net.NetworkInformation;
using GHIElectronics.NETMF.Native;
using GHIElectronics.NETMF.Net.Sockets;

namespace MFConsoleApplication10
{
    public class Program2
    {
        public static void Main()
        {
            // Setup the network
            //ethernet.UseStaticIP("192.168.0.101", "255.255.255.0", "192.168.0.1");
            //Microsoft.SPOT.Net.NetworkInformation.NetworkInterface NI = Microsoft.SPOT.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0];
            //NI.EnableStaticIP("192.168.1.4", "255.255.255.0", "192.168.1.1");

            byte[] mac = { 0x00, 0x26, 0x1C, 0x7B, 0x29, 0xE8 };
            WIZnet_W5100.Enable(SPI.SPI_module.SPI1, (Cpu.Pin)FEZ_Pin.Digital.Di10, (Cpu.Pin)FEZ_Pin.Digital.Di7, true);
            Dhcp.EnableDhcp(mac, "panda");

            Debug.Print("Network settings:");
            Debug.Print("IP Address: " + new IPAddress(NetworkInterface.IPAddress).ToString());
            Debug.Print("Subnet Mask: " + new IPAddress(NetworkInterface.SubnetMask).ToString());
            Debug.Print("Default Getway: " + new IPAddress(NetworkInterface.GatewayAddress).ToString());
            Debug.Print("DNS Server: " + new IPAddress(NetworkInterface.DnsServer).ToString());

            // Start a new thread to listen for incomming connections
            // I believe you can do exactly this in your main function
            new Thread(() =>
            {
                // This code is running is a separate thread.
                Socket socket1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                EndPoint ep1 = new IPEndPoint(IPAddress.Any, 80);
                socket1.Bind(ep1);
                socket1.Listen(1);
                while (true)
                {
                    // Wait for a client to connect
                    Socket client = socket1.Accept();

                    // Client has connected, we hand it off
                    // to another thread to handle the client
                    // communication.
                    ClientConnection.Process(client);
                }
            }).Start();

            Debug.Print("Program Started");

        }
    }

    class ClientConnection
    {
        private const int BufferSize = 1024;

        private Socket _client;
        private byte[] _bytes = new byte[BufferSize];
        private ByteBuffer _buffer = new ByteBuffer();

        public static void Process(Socket client)
        {
            ClientConnection connection = new ClientConnection(client);
            connection.Start();
        }

        private ClientConnection(Socket client)
        {
            _client = client;
        }

        private void Start()
        {
            new Thread(ProcessData).Start();
        }

        private void ProcessData()
        {
            State state = State.ReadLength;
            byte messageLength = 0;

            while (true)
            {
                int bytesReceived = _client.Receive(_bytes);
                for (int i = 0; i < bytesReceived; i++)
                {
                    _buffer.Enqueue(_bytes[i]);
                }

                while (_buffer.Count > 0)
                {
                    switch (state)
                    {
                        case State.ReadLength:
                            if (_buffer.Count >= 1)
                            {
                                messageLength = _buffer.Dequeue();
                                state = State.ReadMessage;
                            }
                            break;

                        case State.ReadMessage:
                            if (_buffer.Count >= messageLength)
                            {
                                byte[] messageBytes = new byte[messageLength];
                                _buffer.Dequeue(messageBytes);
                                Debug.Print(new string(Encoding.UTF8.GetChars(messageBytes)));
                                state = State.ReadLength;
                            }
                            break;
                    }
                }
            }
        }

        enum State
        {
            ReadLength,
            ReadMessage
        }
    }

    public class ByteBuffer
    {
        private byte[] _buffer;
        private int _head;
        private int _tail;
        private int _count;
        private int _capacity;

        private const int _defaultCapacity = 4;

        public ByteBuffer()
        {
            _buffer = new byte[_defaultCapacity];
            _capacity = _defaultCapacity;
            _head = 0;
            _tail = 0;
            _count = 0;
        }

        public int Count
        {
            get { return _count; }
        }

        public void Clear()
        {
            _count = 0;
            _tail = _head;
        }

        public void Enqueue(byte value)
        {
            if (_count == _capacity)
            {
                Grow();
            }
            _buffer[_head] = value;
            _head = (_head + 1) % _capacity;
            _count++;
        }

        public byte Dequeue()
        {
            if (_count == 0) throw new InvalidOperationException("Queue is empty");
            byte value = _buffer[_tail];
            _tail = (_tail + 1) % _capacity;
            _count--;
            return value;
        }

        public int Dequeue(byte[] bytes)
        {
            int maxBytes = System.Math.Min(_count, bytes.Length);
            for (int i = 0; i < maxBytes; i++)
            {
                bytes[i] = Dequeue();
            }
            return maxBytes;
        }

        private void Grow()
        {
            int newCapacity = _capacity << 1;
            byte[] newBuffer = new byte[newCapacity];

            if (_tail < _head)
            {
                Array.Copy(_buffer, _tail, newBuffer, 0, _count);
            }
            else
            {
                Array.Copy(_buffer, _tail, newBuffer, 0, _capacity - _tail);
                Array.Copy(_buffer, 0, newBuffer, _capacity - _tail, _head);
            }

            _buffer = newBuffer;
            _head = _count;
            _tail = 0;
            _capacity = newCapacity;
        }
    }

}

@ taylorza

So, again, thanks for these programming examples. Could I ask a few general TCP questions? You have a good grasp of TCPClient communication, and there are a few points that have always stymied me, and your program is perfect for asking the questions I have. So, I hope you don’t mind that these may be somewhat basic, but here are my hypothetical questions…

A) Shutdown both applications. Start up the sending application (PC). Click the button a few times to send a message. Nothing happens – no error messages. What happened to that data? Where did that data go? Did those go to some queue? Are those dropped frames?

B) Repeat Step A (including sending a few test messages). After sending application (PC) is running, start up receiving application (Panda) and wait for IP to be assigned. In sending application (PC), send another test message. Sending application (PC) hangs, and after a few seconds, produces an error stating that the host communication channel has been shutdown. In the receiving application (Panda), nothing showed up – not the message sent before the receiving application started, neither the message sent after it started. Did it somehow reset that IP on startup and kill any prior connections, or what happened here?

C) Is there a way for the sending application (PC) to somehow test the NetworkStream to see if the channel is connected, or intact, other than waiting for a programmatic response from the receiver (Panda)?

D) Initially, I was thinking that a heartbeat from the Panda to the PC would be the way to go. But, now, Im thinking that a heartbeat from the PC to the Panda makes better sense. That is, upon the Panda receiving an initial command, a timer is started that counts to 3 seconds and takes action (like killing the motors) if another command has not been received and resets the timer. Thoughts?

I’m not going to say I have any level of knowledge that @ taylorza has, but here’s some thoughts.

a). In that scenario, the opening of the channel to the destination (panda) hasn’t completed in the form.load processing; you may need to check the status of the _client object somehow or wrap that setup code elsewhere (personally I’d move it out of the load routine and have another button, or at least a status area telling if it opened the connection properly)

b) seems to be related, except once the device is actually up it is trying to use a connection that isn’t set up and the PC gets a failure that is handled, which is why you’re notified.

c) explore the _client object and methods / properties there? Does BeginConnect have a return code?

d) that’s not a bad approach; it’s like a watchdog, which just says “oh I was doing something now, I can wait a predetermined time before I think I’ve crashed and I should force myself to restart”. All you need to be mindful of is tracking the time of last request or changing your timer callback time every time you get a request.

@ Wayne, @ Brett has provided good answers to all your questions. Here are a few additional points

a) You will notice that in the original code I posted, I have the following


 Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    _client.BeginConnect("192.168.0.101", 80, AddressOf Client_Connect, Nothing)
  End Sub

  Private Sub Client_Connect(ar As IAsyncResult)
    Try
    _client.EndConnect(ar)
      Me.BeginInvoke(Sub()
                       _stream = _client.GetStream()
                       Button1.Enabled = True
                     End Sub)
    Catch ex As Exception
      System.Diagnostics.Debug.WriteLine(ex.ToString())
    End Try
  End Sub

What happens here is that when the Form.Load event fires it asynchronously initiates a TCP connection to the server. At some point the connection to the server will either succeed or fail, in either case the callback ‘Client_Connect’ will be invoked. If the connection failed the call to ‘_client.EndConnect(ar)’ will throw an exception, in my example I am just writing the exception to the debug window, of course in the real application you would be handling this. If the connection succeeded, then and only then am I enabling the button ‘Button1.Enabled = True’. Obviously what you do not see is that ‘Button1’ is initially disabled, this is in the IDE generated code which was not included to keep the sample short. This ensures that only if we successfully establish communication are we able to use the button to send messages.

If the connection is not established ie, the server (Panda) is not running and you try send the data you should be getting an exception telling you that you cannot send data on a connection that is not open. In the example I provided, this would definitely not work, because only if a connection is established will the ‘_stream’ be initialized, so if you are sending data without connecting then you will get a NullReferenceException on ‘_stream.WriteXXX’. Of course the disabled button protects against this.

@ Brett, on this point. While in principal I 100% agree with your point on moving the connection initiation from Form.Load, I agree for different reasons. Keep in mind that ‘_client’ is fully initialized by the time the Form.Load event is raised, it is initialized in the .ctor of the Form, so there is no possibility that it is not initialized by the time Form.Load runs. The reason that I would not have the BeginConnect in the Form.Load is purely to provide a means for the user to actually initiate the connection after taking some corrective action. Currently the application would need to be restarted after the corrective action is taken. Reporting the status would happen from the ‘Client_Connect’ callback because the connection is being initiated asynchronously anyway, so at the point that the connection is “started” there is no status, BeginConnect returns immediately and it is not good to block it with a subsequent wait, which defeats the purpose of doing it async in the first place so no messages should be send out on to the network.

b) Unless you try to re-establish the connection, all attempts to send messages on the connection will fail. TCP/IP is a connection based protocol, so step one is to establish a connection, if that fails nothing else succeeds. When the Panda is started, you will need to attempt to re-establish the connection, and if it is connected then the sending of data should succeed.

c) Technically no, the Connected property is misleading. In truth it only tells you the state of the connection at the last read/write. If the connection is interrupted and there is not a pending read then the value of Connected is only the last state of the connection. The only way to know for sure if a socket connection is up is if you can send/receive data on it. Of course this point is often missed because typically you always have a read scheduled on the socket, so if the connection is closed correctly then the read will return -1 or throw an exception. Robust TCP code should always check for -1 handle ObjectDisposedException and of course SocketException. ObjectDisposedException occurs depending on the state of the code at the point the connection is closed on the other side. Closing a socket correctly is actually serious business and more often than not the correct sequence is not followed. I believe TcpClient actually performs a graceful shutdown, to be honest, I readly use TcpClient and TcpListener the raw Socket class is my preference, but that is actually a little silly I am sure TcpClient/TcpListener are very good it is just that I often do native sockets and prefer the code parity between the two.

d) I 100% agree with Brett, this sounds like a good approach. So you have the Panda reset the timer every time a message is received from the PC, on the PC you can have a timer that is reset every time you send a message, if the PC timer expires you send a ‘null’ message. This way the Panda gets a message and resets it’s timer, and the PC is indicating that it is still alive. Assuming of course that is your requirement, if you expect the PC to be continuously sending messages then the PC side timer is not required, it just depends if the PC messages are actually ad-hoc messages and all the Panda is concerned with is that the PC is actually there to send it messages.

Wow, that was a lot of typing. I hope I did not miss anything or misunderstand any of your questions and provide totally unrelated answers. If I did, please feel free to ask.

@ Brett & @ taylorza: Thanks for the answers – they cleared up three of my four questions…I had a follow-on question on the first question.

A) Sorry to be hard-headed on this question, but heres my follow-on question.

@ taylorza: if I run the PC application (even if the Panda isnt running and is powered off), the form loads and the button activates and theres no error in the Output or Immediate windows…suggesting that the BeginConnect executed successfully and connected to 192.168.1.4 (Panda). I can even click the button and theres still no error…suggesting that data got sent successfully to 192.168.1.4 (Panda). …but, theres no device with 192.168.1.4 (the Panda is still off). Not sure what got connected and wheres the data going. Any ideas?

@ Brett: checking the status of the _client is exactly what I want to do…I just dont know how. I thought that the Client_Connect does that, but it provides a successful result even when the connection definitely didnt occur (the Panda is off).

B) @ taylorza & @ Brett: This explains the behavior – either re-establish the connection, or get an error. Im curious if the Panda, upon capturing IP 192.168.1.4, sends some sort of signal to all devices that had pre-established connections.

C) @ taylorza: I totally get the issue with the Connected property. Actually, I read something similar somewhere, so youve confirmed it – the Connected property is misleading and only provides the last status. Thus, the only way to check the connection is to do a write/read action and check the result of it.

D) @ taylorza & @ Brett: Im going to use the method we discussed. Sounds like we all agree its a decent method.

Okay, after some additional testing, I’ve discovered a modification to question A: Client_Connect always provides a successful result from the BeginConnect command in Form1_Load routine. However, clicking the button to send a message will provide an error if, after 30 seconds, the button is clicked again. For example…

(assumes the Panda is not running anything and powered off)

  1. start the application: no error…even after time.
  2. click the button 1st time: no error…even after time.
    …now…
    3a) click the button 2nd time within 30 seconds: no error…even after time.
    3b) click the button 2nd time beyond 30 seconds: system error.