Socket Connection & Buffering Packets

Hi guys,

Need a little help with my program.
I made a C# project (windows forms) that connects to the VelbusLink in server-mode.
As long as I test the application on the PC running the VelbusLink it works without a problem.
But when I try the program on another PC in the network, most of the packets are received OK, but some packets are misformed.

For example when I run one instance of the application on the PC with VelbusLink and another instance on a remote PC I get this:
Local:
http://img31.imageshack.us/img31/8523/knipsely.png

Remote:
http://img10.imageshack.us/img10/8572/knipsel2.png

I presume this is a buffer issue, this is my code for the socket client:

[code]using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;

namespace VelbusServer
{
public partial class VelbusConnector : UserControl
{
public VelbusConnector()
{
InitializeComponent();
}

    private Socket s;
    private byte] receivebuffer = new byte[20];

    public delegate void PacketReceivedHandler(object velbusBroker, ReceivedPacketArgs packetInfo);
    public event PacketReceivedHandler PacketReceived;

    public void Disconnect()
    {
        s.Disconnect(false);
        s.Close();
    }

    protected void OnPacketReceived(object velbusBroker, ReceivedPacketArgs packetInfo)
    {
        if (PacketReceived != null)
        {
            PacketReceived(velbusBroker, packetInfo);
        }
    }

    public void Connect(string server, int port)
    {
        try
        {
            IPHostEntry hostEntry = null;
            hostEntry = Dns.GetHostEntry(server);
            IPAddress resolvedIP = null;
            foreach (IPAddress ip in hostEntry.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    resolvedIP = ip;
                }

            }
            IPEndPoint ipe = new IPEndPoint(resolvedIP, port);
            s = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

            s.Connect(ipe);

            s.BeginReceive(receivebuffer, 0, receivebuffer.Length, SocketFlags.None, new AsyncCallback(OnDataReceived), null);
        }
        catch
            {
                MessageBox.Show("Kan niet verbinden, is de Server online?");
            }
    }

    private void OnDataReceived(IAsyncResult res)
    {

        string strPacket = "";

        for (int i = 0; i < 15; i++)
        {
            strPacket = strPacket + " " + receivebuffer*.ToString("X2");
        }

        if (s != null && s.Connected)
        {
            s.BeginReceive(receivebuffer, 0, receivebuffer.Length, SocketFlags.None, new AsyncCallback(OnDataReceived), null);
        }

        ReceivedPacketArgs packet = new ReceivedPacketArgs(receivebuffer);

        OnPacketReceived(this, packet);
    }

    public class ReceivedPacketArgs : EventArgs
    {
        public ReceivedPacketArgs(byte] packet)
        {
            this.packet = packet;
        }
        public readonly byte] packet;
    }


    public void SendPacket(byte] packet)
    {
        s.Send(packet);      
    }
}

}
[/code]

I tried with different buffer sizes but result is the same.

Can someone shed some light on how to correctly receive the packets over a socket connection?

Thanks!*

As I’m not a C# programmer, I couldn’t give you any help :blush: but like I’m PureBasic programmer, I know some limitation : :bulb: maybe the NULL character is used as end of trame for s.BeginReceive procedure or something like this…

Cantryn,

BeginReceive is an asynchronous operation, meaning that you initiate a read and don’t like to wait until it finishes to continue your application. The callback you pass to that function will instead be called when the read is complete.

But, where there’s a beginning there’s an end so every BeginReceive requires and **EndReceive **(missing in yours). EndReceive is called inside the callback method to finalize the read operation and make the data available to your application. EndReceive also returns the number of bytes read, which may or may not be the number of bytes you requested. So don’t assume you will have received 16 bytes every time your callback is called. Also take into account that one packet may be spread over different reads, so you will require a more advanced parser that can handle this type of situation (which will happen more frequently if you work with a remote computer).

As a last remark:

So beware of multithreading issues!

It all sounds complicated but i’m sure you’ll get it to work, just solve the EndReceive problem and google around a bit. If you want a parser you can try the PacketParser from our library.

Ok, thanks for the info.

I made a new project and now I got this, it seems to work.

[code]using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using Velleman.Velbus;

namespace SocketReceiverTest
{
public partial class Form1 : Form
{

    byte] m_DataBuffer = new byte[100];
    IAsyncResult m_asynResult;
    public AsyncCallback pfnCallBack;
    public Socket m_socClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    PacketParser pp = new PacketParser();

    // create the socket...
    public void OnConnect()
    {
        // get the remote IP address...
        IPAddress ip = IPAddress.Parse("10.66.45.70");
        int iPortNo = 3788;
        //create the end point
        IPEndPoint ipEnd = new IPEndPoint(ip.Address, iPortNo);
        //connect to the remote host...
        m_socClient.Connect(ipEnd);
        //watch for data ( asynchronously )...
        WaitForData();
    }
    public void WaitForData()
    {
        if (pfnCallBack == null)
            pfnCallBack = new AsyncCallback(OnDataReceived);
        // now start to listen for any data...
        m_asynResult =
        m_socClient.BeginReceive(m_DataBuffer, 0, m_DataBuffer.Length, SocketFlags.None, pfnCallBack, null);
    }
    public void OnDataReceived(IAsyncResult asyn)
    {
        //end receive...
        int iRx = 0;
        iRx = m_socClient.EndReceive(asyn);
        byte] buffer2 = new byte[iRx];
        buffer2 = m_DataBuffer;

        pp.Feed(m_DataBuffer, iRx);

        WaitForData();
    }


    private void WriteToLog(string message)
    {

        if (listBox1.InvokeRequired)
            listBox1.BeginInvoke(new MethodInvoker(delegate() { WriteToLog(message); }));
        else
            listBox1.Items.Insert(0, message);
    }

    public Form1()
    {
        InitializeComponent();
        timer1.Start();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        OnConnect();
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        m_socClient.Disconnect(false);
        m_socClient.Close();
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        try
        {
            string szData = "";
            if (m_socClient.Connected)
            {
                szData = BitConverter.ToString(pp.Next().Pack());
                WriteToLog(szData);
            }
        }
        catch { }
    }
}

}
[/code]

Question:
Is it possible to get the state of the PacketParser buffer, so I can run the next method untill the buffer is empty, I use a timer now but it’s a little dirty because it trows a Null Exception if the buffer is emty.

The parser will return null if it’s unable to parse a complete packet from the current buffer. Everytime you feed data one or more new packets may become available, so you need to loop like this:

pp.Feed(m_DataBuffer, iRx); // feed received data to the parser

Packet packet = pp.Next(); // try to parse a packet
while (packet != null) // did we succeed in parsing a packet?
{
    WriteToLog( BitConverter.ToString(pp.Next().Pack()) );
    packet = pp.Next(); // try to parse another packet
}

So no need for the timer, just do this in your OnDataReceived event.

Good luck, you’re doing great!

Ok thanks, that does it.

Code for my VelbusConnector user control, hope someone can use this:

[code]using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using Velleman.Velbus;

namespace VelbusServer
{
public partial class VelbusConnector : UserControl
{

    IAsyncResult m_asynResult;
    public AsyncCallback pfnCallBack;

    private Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    private PacketParser pp = new PacketParser();

    private byte] receivebuffer = new byte[100];

    public delegate void PacketReceivedHandler(ReceivedPacketArgs packetInfo);
    public event PacketReceivedHandler PacketReceived;

    public VelbusConnector()
    {
        InitializeComponent();
    }

    public void Connect(string server, int port)
    {
        try
        {
            IPHostEntry hostEntry = null;
            hostEntry = Dns.GetHostEntry(server);
            IPAddress resolvedIP = null;
            foreach (IPAddress ip in hostEntry.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    resolvedIP = ip;
                }

            }
            IPEndPoint ipe = new IPEndPoint(resolvedIP, port);

            s.Connect(ipe);

            WaitForData();
        }
        catch
        {
            MessageBox.Show("Kan niet verbinden, is de Server online?");
        }
    }

    public void Disconnect()
    {
        s.Disconnect(false);
        s.Close();
    }

    public void WaitForData()
    {
        if (pfnCallBack == null)
            pfnCallBack = new AsyncCallback(OnDataReceived);
        // now start to listen for any data...
        m_asynResult =
        s.BeginReceive(receivebuffer, 0, receivebuffer.Length, SocketFlags.None, pfnCallBack, null);
    }

    private void OnDataReceived(IAsyncResult res)
    {
        //end receive...
        int iRx = 0;
        iRx = s.EndReceive(res);

        pp.Feed(receivebuffer, iRx); // feed received data to the parser

        Packet packet = pp.Next(); // try to parse a packet

        while (packet != null) // did we succeed in parsing a packet?
        {
            ReceivedPacketArgs ra = new ReceivedPacketArgs(packet);

            if (PacketReceived!=null)
            PacketReceived(ra);  // raise event

            packet = pp.Next(); // try to parse another packet
        }

        WaitForData();
    }

    public class ReceivedPacketArgs : EventArgs
    {
        public ReceivedPacketArgs(Packet packet)
        {
            this.packet = packet;
        }
        public readonly Packet packet;
    }


    public void SendPacket(Packet packet)
    {
        s.Send(packet.Pack());      
    }

}

}
[/code]

My Velbus connector is running without problems at the moment.
But (and this is more a programming question then a Velbus question) is it possible to close the socket connection when the remote computer goes to sleep, because now it just loses connection (obviously) and Velbuslink on the server crashes…

Oh, and I changed the buffer size to 512, with a buffer of 100 bytes I was still missing packets. So if anyone wants to use the code posted in my post above please keep this in mind.