none
bad TCP Sock communication RRS feed

  • Question

  •  ok here is my Wire-shark :

    my program is on 202 and it is reading from 5.1 

    the connect looks good : SYN ,  SYN ACK, ACK

    but when I am pulling data that does not look right

    it should be  modus, modus, tcp ack , modus, modus, tcp ack

    but it is not..

    here is my connection code:

     public Boolean OpenConnection(Logs.LogType t)
            {
                bool good= false;
               
                    try
                    {
                    if (sck == null)
                    {
                        sck = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        TCP_Connect(t);
                        Transaction_Identifier = 0;
                        good = true;
                    }
                    else
                    {
                        good = true;
                    }
                    }
                    catch (Exception ex)
                    {
                        good = false;
                        Logs.HandleException(ex, t);
                    }
                
                return good;
            }
    
    
            void TCP_Connect(Logs.LogType t)
            {
                try
                {
    
                    localEndPoint = new IPEndPoint(IPAddress.Parse(Communication.IPAddress), Convert.ToInt16(Communication.PortNum));
                    sck.SendTimeout = 6000;
                    sck.ReceiveTimeout = 6000;
                    sck.Connect(localEndPoint);
                   
                }
                catch (Exception e)
                {
                    Logs.HandleException(e, t);
                    return;
                }
                
            }

    and here is my data pulling code :

     public ushort[] SendFunction1(string address, string number, string SlaveAddress)
            {
                Transaction_Identifier++;
                byte[] array_out = new byte[12];
                ushort[] temp = new ushort[1];
                byte[] bytes = BitConverter.GetBytes(Transaction_Identifier);
                try
                {
                    byte[] bytes2 = BitConverter.GetBytes(ushort.Parse(number));
                    byte[] bytes3 = BitConverter.GetBytes(ushort.Parse(address));
                    array_out[0] = bytes[1];
                    array_out[1] = bytes[0];
                    array_out[2] = 0;
                    array_out[3] = 0;
                    array_out[4] = 0;
                    array_out[5] = 6;
                    array_out[6] = Convert.ToByte(SlaveAddress);// 01; // The Slave Address
                    array_out[7] = 03; // The Function Code
                    array_out[8] = bytes3[1]; // The Data Address  
                    array_out[9] = bytes3[0];// The Data Address  
                    array_out[10] = bytes2[1];// The total number of registers requested
                    array_out[11] = bytes2[0];// The total number of registers requested
                    Send(array_out, 200);
    
    
    
                    temp[0] = Receive(200, address);
                    return temp;
                }
                catch (Exception ex)
                {
                    Logs.WriteLog(ex.Message, NSpring.Logging.Level.Exception, Logs.LogType.Modbus);
                    return null;
                }
              
            }
    
            public void Send(byte[] buffer, int timeout)
            {
                int startTickCount = Environment.TickCount;
                int sent = 0;  // how many bytes is already sent
                sck.SendTimeout = 500;
                sck.ReceiveTimeout = 7000;
                try
                {
                    sent = sck.Send(buffer);
                    //sck.Close();
    
                }
                catch (SocketException ex)
                {
                    string temp = " Error :" + ex.Message + Environment.NewLine;
                    Logs.WriteLog(temp, NSpring.Logging.Level.Exception, Logs.LogType.Modbus);
                }
            }
    
    
            public ushort Receive(int timeout, string address)
            {
                byte[] buffer = new byte[1064];
                byte[] buffer2;
               
                //Socket received;
                try
                {
                    int temp = sck.Receive(buffer);
                    buffer2 = new byte[temp];
                    for (int i = 0; i < temp; i++)
                    {
                        buffer2[i] = buffer[i];
                    }
                    if (buffer2.Length == 9)
                    {
                        string temp1 =  DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss tt") + "Exception code: " + buffer[8] + Environment.NewLine;
                        Logs.WriteLog(temp1, NSpring.Logging.Level.Exception, Logs.LogType.Modbus);
                        return 0;
                    }
                    ushort intTemp = Convert.ToUInt16(((buffer2[9] << 8) + buffer2[9 + 1]));
                    return intTemp;
                }
                catch (SocketException ex)
                {
                   
                    Logs.WriteLog(ex.Message, NSpring.Logging.Level.Exception, Logs.LogType.Modbus);
                    return 0;
                }
            }
    
           
    

    can someone tell me what i am doing wrong ?

    Thursday, September 24, 2015 3:22 PM

Answers

  • Hi Btb4198,

    Based on your code, looks you want to implement the receive by yourself.

    We often start to reads data from NetworkStream, and also write to the NetworkStream. So I would suggest you use .NET reflector tool to reflect  NetworkStream class.

    private void button1_Click(object sender, EventArgs e)
            {
                NetworkStream serverStream = clientSocket.GetStream();
                byte[] outStream = System.Text.Encoding.ASCII.GetBytes(textBox2.Text + "$");
                serverStream.Write(outStream, 0, outStream.Length);
                serverStream.Flush();
    
                byte[] inStream = new byte[10025];
                serverStream.Read(inStream, 0, (int)clientSocket.ReceiveBufferSize);
                string returndata = System.Text.Encoding.ASCII.GetString(inStream);
                msg(returndata);
                textBox2.Text = "";
                textBox2.Focus();
            }

    Have a nice day!

    Kristin


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Monday, September 28, 2015 2:24 PM

All replies

  • Can you be more specific about where you think the problem lies?  We don't understand your format so we cannot identify protocol issues for you.  I can tell you that the receive code doesn't look correct to me.

    public ushort Receive(int timeout, string address)
    {
       byte[] buffer = new byte[1064];
       byte[] buffer2;
       
       //This reads the next set of available data into buffer and returns the # of bytes read, there is no guarantee that it read all the data that was sent, you have to keep looping until you receive all the data you expected        
       int temp = sck.Receive(buffer);
    
       //Copy the data that is already in a buffer to another buffer, why?
       buffer2 = new byte[temp];
       for (int i = 0; i < temp; i++)
       {
          buffer2[i] = buffer[i];
       }
    
       //If the buffer happens to be 9 bytes long then assume an error   
       if (buffer2.Length == 9)
       {
          ...
       };
    
       //Take the 10th byte, shift it, add the 11th byte and convert that to a ushort
       ushort intTemp = Convert.ToUInt16(((buffer2[9] << 8) + buffer2[9 + 1]));
       return intTemp;
    }

    The problems that I see are as follows:

    1. You're making the assumption that Receive gets all the data you expect/sent and there are no guarantees. You have to know how much to receive and you have to keep calling Receive until you get it.  Most protocols use either a fixed size header or pass the length of the data as one of the first data points.
    2. You are taking a byte and trying to convert it to a ushort. I would personally recommend that you simply use BitConverter to convert the existing data in buffer2, starting at the appropriate offset back.  The only issue you'll run into is if the data was sent using big endian as the converter assumes little endian.
    3. You're creating a huge buffer to read 1 piece of data and then you're copying that into a temp buffer which is inefficient.  Create a buffer, Receive into that and then work on the data using the buffer.
    4. If possible consider using BinaryReader to read the data from the buffer rather than doing this by hand.  It will simplify your code. The only thing to be aware of is that the reader closes the underlying stream when it is done but if you're simply wrapping a binary array that should be fine.

    Michael Taylor
    http://blogs.msmvps.com/p3net

    Thursday, September 24, 2015 4:07 PM
    Moderator
  • Michael Taylor

    are you saying I am not get all the data in the buffer ?

    how do I know how much data I should get ?

    How do i send out an ack saying I got the data ?

    Thursday, September 24, 2015 4:15 PM
  • what is BinaryReader?

    I do not see a 

    sck.BinaryReader

    Thursday, September 24, 2015 4:16 PM
  • I really think I need to send out an ack after I get the data from the server

    how do I do that ?

    Thursday, September 24, 2015 4:44 PM
  • "how do I know how much data I should get ?"

    There is no way of knowing, it is completely dependent upon the protocol you are using.  As I already mentioned, most protocols use either a message header that contains the length of the data or pass the length of the data as the first piece of data. The only thing that is fixed is the header size or the length size. 

    "I really think I need to send out an ack after I get the data from the server"

    There is no such thing as an ACK in socket communication.  What you have to do is completely dependent upon the protocol that you and the remote device use.  Every protocol is different.  You need to use the documentation that comes with the device to determine how to send an ACK back to it, if it is required by the protocol. 

    "what is BinaryReader?"

    It is a type (System.IO.BinaryReader). It's purpose is to sit on top of a stream (network, file, whatever) and provide convenient methods for reading formatted data from the stream rather than having you write the convert calls yourself.  You don't have to use it, it just makes things easier.

    You really need to dig into the documentation for the device you're communicating with to understand the protocol. There is no way around it.  Every device is different and there is no 1 protocol that all devices use. It looks like you're trying to talk to the Modbus PLC. In that case you need to refer to their documentation on how to communicate with the device. It will be completely different than talking to, say, an AB device or an OPC device.  Here's one such protocol that Modbus provided but it may or may not apply to your device.  You have to identify the structure that the protocol requires and then you can break it down into C# code to send that structured data. 

    Personally when I had to do this in a past life I created a structure in C# that matches the header needed by the protocol.  I then used BinaryReader/BinaryWriter to read/write the entire structure.  Since the data length was part of the header it was easy to read the entire header into memory and then read the remaining data based upon what the header said.  This is pretty standard for socket protocols.

    Thursday, September 24, 2015 5:08 PM
    Moderator
  • sorry i do not think i was saying this right

    I am using TCP. how do i get my program to send a TCP ACK after I read data ?

    Thursday, September 24, 2015 5:16 PM
  • You're saying it correctly.  The underlying TCP infrastructure already handles ACKs for managing bidirectional communication.  You don't do anything for that.  The issues you're having, I believe, is because you're not receiving the data properly and/or not following the protocol rules for the device. 

    Another thing to keep in mind is that the remote end doesn't know whether you processed data correctly or not so you need to also ensure that you clear out any remaining data before receiving a new message.  Again, many protocols use a message header to solve this.  Headers tend to have a signature or some other value that you can use to "resync" received data when you haven't correctly read the previous data. This is all part of creating an error-resilient network protocol. 

    Thursday, September 24, 2015 5:19 PM
    Moderator
  • I need to sent an ack to the device after I read the data. This is how Mod bus over TCP work. but my code is not sending the ack. I know because of wireshark... how do I get it to send a TCP ack after it reads ?
    • Edited by Btb4198 Thursday, September 24, 2015 5:25 PM
    Thursday, September 24, 2015 5:24 PM
  • It sends one on connection ... I just need it to do that after it pull data too...how do it do that ?
    • Edited by Btb4198 Thursday, September 24, 2015 5:30 PM
    Thursday, September 24, 2015 5:25 PM
  • I'm going to go out on a limb and say that it'll send an ACK once you've read all the data that has been sent by the device.  Since your read logic isn't reading all the data then no ACK is being sent.  Fix your receive code and see if things start working.
    Thursday, September 24, 2015 5:51 PM
    Moderator
  • it does read all the data...  it reads all 64 bytes...

    I know because I am using wire Shark
    • Edited by Btb4198 Thursday, September 24, 2015 5:55 PM
    Thursday, September 24, 2015 5:54 PM
  • Sorry I said that wrong. I am getting all the bytes in.

    I am getting in 11 bytes:

    0 1 0 0 0 5 37 3 2 0 110   

    This is Modbus over TCP  before the 5 is a the header, the 5 is how much data I am getting...

    if you do not believe me, please read this link 

    http://www.simplymodbus.ca/TCP.htm

    ok so I am reading all the bytes in

    but the program it not sending a tcp ack.

    why? 




    • Edited by Btb4198 Thursday, September 24, 2015 6:05 PM
    Thursday, September 24, 2015 6:04 PM
  • I am not aware of any way to send an explicit TCP ACK as this is a low level detail. If you're using a network analyzer then you might see it but it is part of the low level details, not something the code is doing explicitly. This is discussed over at SO if you're interested.

    I have no way of testing this since I don't have a Modbus device but the header format you linked to is defined as:

    public struct ModBusHeader
    {
       public ushort TransactionId { get; set; }
       public ushort ProtocolId { get; set; }
       public ushort Length { get; set; }
       public byte UnitId { get; set; }
    }

    As such I would first receive in enough data to read the header.  Then using Length read in the remaining data as a byte array.  Given your original data of 0 1 0 0 0 5 37 3 2 0 110 I translate that as follows:

    TransactionId = 0001
    ProtocolId = 0000
    Length = 0005
    UnitId = 37
    Data = 3 2 0 110

    The only part that seems funny to me is that the Length includes the unit ID even though that is part of the header. That lines up with what the example doc said. As such you'd end up having to subtract 1 from the length to determine how much data to read.  This is also assuming that you're in ADU mode and not RTU.  I have no idea how to confirm that since it appears that either model can be used over TCP.  If you're getting all the data correctly then I think everything is OK.  Based upon what the docs said the server is basically in listen mode so when it gets a message, it sends a response and then waits again. As such an ACK doesn't seem necessary.  Have you tried sending another message and if so what happened?  Can you post the actual send/receive code that you're using now because the original code doesn't line up with what you're describing as doing now?

    Thursday, September 24, 2015 6:33 PM
    Moderator
  •   public ushort[] SendFunction1(string address, string number, string SlaveAddress)
            {
                Transaction_Identifier++;
                byte[] array_out = new byte[12];
                ushort[] temp = new ushort[1];
                byte[] bytes = BitConverter.GetBytes(Transaction_Identifier);
                try
                {
                    byte[] bytes2 = BitConverter.GetBytes(ushort.Parse(number));
                    byte[] bytes3 = BitConverter.GetBytes(ushort.Parse(address));
                    array_out[0] = bytes[1];
                    array_out[1] = bytes[0];
                    array_out[2] = 0;
                    array_out[3] = 0;
                    array_out[4] = 0;
                    array_out[5] = 6;
                    array_out[6] = Convert.ToByte(SlaveAddress);// 01; // The Slave Address
                    array_out[7] = 03; // The Function Code
                    array_out[8] = bytes3[1]; // The Data Address  
                    array_out[9] = bytes3[0];// The Data Address  
                    array_out[10] = bytes2[1];// The total number of registers requested
                    array_out[11] = bytes2[0];// The total number of registers requested
                    Send(array_out, 200);
    
    
    
                    temp[0] = Receive(200, address);
                    return temp;
                }
                catch (Exception ex)
                {
                    Logs.WriteLog(ex.Message, NSpring.Logging.Level.Exception, Logs.LogType.Modbus);
                    return null;
                }
              
            }
    
            public void Send(byte[] buffer, int timeout)
            {
                int startTickCount = Environment.TickCount;
                int sent = 0;  // how many bytes is already sent
                sck.SendTimeout = 500;
                sck.ReceiveTimeout = 7000;
                try
                {
                    sent = sck.Send(buffer);
                    //sck.Close();
    
                }
                catch (SocketException ex)
                {
                    string temp = " Error :" + ex.Message + Environment.NewLine;
                    Logs.WriteLog(temp, NSpring.Logging.Level.Exception, Logs.LogType.Modbus);
                }
            }
    
    
            public ushort Receive(int timeout, string address)
            {  
                //Socket received;
                try
                {
                    int temp =0;
                    
                    int act = sck.Available;
                    byte[] buffer = new byte[act];
                    while ( temp <act)
                    {
                        temp = temp + sck.Receive(buffer);
                    }
                    
                    if (buffer.Length == 9)
                    {
                        string temp1 =  DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss tt") + "Exception code: " + buffer[8] + Environment.NewLine;
                        Logs.WriteLog(temp1, NSpring.Logging.Level.Exception, Logs.LogType.Modbus);
                        return 0;
                    }
                    ushort intTemp = Convert.ToUInt16(((buffer[9] << 8) + buffer[9 + 1]));
                    return intTemp;
                }
                catch (SocketException ex)
                {
                   
                    Logs.WriteLog(ex.Message, NSpring.Logging.Level.Exception, Logs.LogType.Modbus);
                    return 0;
                }
            }

    Thursday, September 24, 2015 6:43 PM
  • this is how it works with modscan32  ( you can download it online)

    Thursday, September 24, 2015 6:49 PM
  • see that is how it suppose to work

    it should send an ack after it reads the data...

    but my program is not...

    how can I get it to send an ack ?

    Thursday, September 24, 2015 6:51 PM
  • Assuming all the data is received together then you're fine but otherwise you're still not reading all the data correctly. Ignoring errors for the moment, you need to keep reading until you've read in the header. This may or may not match Available. Once you've read the entire header in then you know how much data is remaining so read the rest of that into a byte array that you can then process.

    Note: Untested code

    public static class ModBusExtensions
    {        
        public static ModBusHeader ReceiveModBusHeader ( this Socket socket )
        {
            var buffer = new byte[7];
            int totalRead = 0;
                
            do
            {
                int read = socket.Receive(buffer, totalRead, buffer.Length - totalRead, SocketFlags.None);
                if (read == 0)
                    throw new Exception("Failed to read header.");
            } while (totalRead < buffer.Length);
    
            using (var reader = new BinaryReader(new MemoryStream(buffer)))
            {
                var header = new ModBusHeader() {
                    TransactionId = reader.ReadUInt16(),
                    ProtocolId = reader.ReadUInt16(),
                    Length = reader.ReadUInt16(),
                    UnitId = reader.ReadByte()                    
                };
    
                //Should do some validation
                header.Data = reader.ReadBytes(header.Length - 1);
                return header;
            };
        }
    }
    
    public struct ModBusHeader
    {
        public ushort TransactionId { get; set; }
        public ushort ProtocolId { get; set; }
        public ushort Length { get; set; }
        public byte UnitId { get; set; }
    
        public byte[] Data { get; set; }
    }

    Thursday, September 24, 2015 7:06 PM
    Moderator
  • "see that is how it suppose to work"

    That really means nothing to me.  You're using a tool that is looking at the low level TCP packet data.  Between your app and the low level packet is the network stack.  Some of the stuff that is being sent is not something the app is responsible for.  AFAIK the ACK is probably that.

    Take a look at an existing implementation that somebody else wrote for Modbus TCP in .NET.  I cannot vouch for how good it is but it may give you an idea of how they are doing things.

    Thursday, September 24, 2015 7:09 PM
    Moderator
  • I do read all the data... you keep saying I am not, but I do ...

    I am even using the 

    sck.Available;
    Thursday, September 24, 2015 7:50 PM
  • but it is still not sending the ack
    Thursday, September 24, 2015 7:53 PM
  • I'm afraid I've run out of suggestions for you.  I wrote code to talk to PLCs for several years and I never had this issue.  The Socket class (or more correctly the TcpClient class) did everything correctly.  The only time I ever had issues is when data was partially processed.

    I'll let others step and provide suggestions to you now.  Perhaps they can identify where your problem resides.  Good luck.

    Thursday, September 24, 2015 8:05 PM
    Moderator
  • anyone else ???

    Please

    Thursday, September 24, 2015 9:13 PM
  • How do I get  my Socket class to return an ack reply after it is done reading in bytes ?

    this is my connection code:

    public Boolean OpenConnection(Logs.LogType t)
            {
                bool good= false;
               
                    try
                    {
                    if (sck == null)
                    {
                        sck = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        TCP_Connect(t);
                        Transaction_Identifier = 0;
                        good = true;
                    }
                    else
                    {
                        good = true;
                    }
                    }
                    catch (Exception ex)
                    {
                        good = false;
                        Logs.HandleException(ex, t);
                    }
                
                return good;
            }
    
    
            void TCP_Connect(Logs.LogType t)
            {
                try
                {
    
                    localEndPoint = new IPEndPoint(IPAddress.Parse(Communication.IPAddress), Convert.ToInt16(Communication.PortNum));
                    sck.SendTimeout = 6000;
                    sck.ReceiveTimeout = 6000;
                    sck.Connect(localEndPoint);
                   
                }
                catch (Exception e)
                {
                    Logs.HandleException(e, t);
                    return;
                }
                
            }
    
    This is my receive code
      public ushort Receive(int timeout, string address)
            {  
                //Socket received;
                try
                {
                    int temp =0;
                    
                    int act = sck.Available;
                    byte[] buffer = new byte[act];
                    while ( temp <act)
                    {
                        temp = temp + sck.Receive(buffer);
                    }
                    
                    if (buffer.Length == 9)
                    {
                        string temp1 =  DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss tt") + "Exception code: " + buffer[8] + Environment.NewLine;
                        Logs.WriteLog(temp1, NSpring.Logging.Level.Exception, Logs.LogType.Modbus);
                        return 0;
                    }
                    ushort intTemp = Convert.ToUInt16(((buffer[9] << 8) + buffer[9 + 1]));
                    return intTemp;
                }
                catch (SocketException ex)
                {
                   
                    Logs.WriteLog(ex.Message, NSpring.Logging.Level.Exception, Logs.LogType.Modbus);
                    return 0;
                }
            }

    I need the program to send out a ack after this is done read in the data and it is not doing that

    how do I make it do that ?

    Friday, September 25, 2015 1:08 PM
  • Hi Btb4198,

    Based on your code, looks you want to implement the receive by yourself.

    We often start to reads data from NetworkStream, and also write to the NetworkStream. So I would suggest you use .NET reflector tool to reflect  NetworkStream class.

    private void button1_Click(object sender, EventArgs e)
            {
                NetworkStream serverStream = clientSocket.GetStream();
                byte[] outStream = System.Text.Encoding.ASCII.GetBytes(textBox2.Text + "$");
                serverStream.Write(outStream, 0, outStream.Length);
                serverStream.Flush();
    
                byte[] inStream = new byte[10025];
                serverStream.Read(inStream, 0, (int)clientSocket.ReceiveBufferSize);
                string returndata = System.Text.Encoding.ASCII.GetString(inStream);
                msg(returndata);
                textBox2.Text = "";
                textBox2.Focus();
            }

    Have a nice day!

    Kristin


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Monday, September 28, 2015 2:24 PM