Modbus communication over tcp doesn't send data to all devices - c#

I have a gateway connected to a computer which runs a C# program. I have a Modbus slave-master relation between computer and the gateway. Here are the gateway configurations (model EKI-1221-BE):
The set up I have is the same as in this picture but I have 6 devices connected instead of 2 and will later have 12:
I can send a Modbus command to any of the 6 devices without any problems but the issue happens when I try to send multiple commands in quick interval. In the following code, only the device 1,2 and 6 receive the command.
PumpsComm.SendMessage(1, 3099, 2000);
PumpsComm.ClientSocket.Receive(TempBuffer);
PumpsComm.SendMessage(2, 3099, 3000);
PumpsComm.ClientSocket.Receive(TempBuffer);
PumpsComm.SendMessage(3, 3099, 4000);
PumpsComm.ClientSocket.Receive(TempBuffer);
PumpsComm.SendMessage(4, 3099, 6000);
PumpsComm.ClientSocket.Receive(TempBuffer);
PumpsComm.SendMessage(5, 3099, 8000);
PumpsComm.ClientSocket.Receive(TempBuffer);
PumpsComm.SendMessage(6, 3099, 10000);
PumpsComm.ClientSocket.Receive(TempBuffer);
The function SendMessage is as follows, where ClientSocket is a TCP Socket that is connected to the gateway / Modbus slave.
public void SendMessage(int pumpID, int register, int command)
{
byte[] message = new byte[12];
message[0] = 0;
message[1] = 0; //Message number
message[2] = 0;
message[3] = 0;
message[4] = 0;
message[5] = 6; //Message length
message[6] = (byte)ToHexInDec(pumpID, 'L'); //Pump ID
message[7] = 6;
message[8] = (byte)ToHexInDec(register, 'H'); //Register high
message[9] = (byte)ToHexInDec(register, 'L'); //Register low
message[10] = (byte)ToHexInDec(command, 'H'); //Command high
message[11] = (byte)ToHexInDec(command, 'L'); //Command low
try
{
ClientSocket.Send(message);
}
catch
{
return;
}
}
If I make the Thread sleep for 30ms between each send, every command works but this is not an option in my case as I need to be able to update every device in a very short amount of time.
I looked at the packets that I was sending and receiving to and from the gateway with Wireshark and I think I found where the
This is what the output of Wireshark was (the Ip that ends with 200 is the computer and the one that ends with 107 is the gateway):
It looks as though the communication for the device 3,4,5 and 6 where put in the same TCP packet and for some reason, the only Modbus communication that was read from that packet is the last one.
I tried adding MBAP transaction numbers for every communication in the MBAP header of the Modbus communication but it didn't change anything. I then tried to add a blocking receive that makes the program wait for the Modbus response before sending the next communication but I am not receiving anything (even though we can see that the gateway is sending a Modbus response back when the communication was successfull)
I don't know what I can do anymore and was looking to see if anyone with more experience with TCP modbus communications could help. I can provide more details if necessary. Sorry if this is not clear enough, english is not my first language.
Thanks!

As per the spec
Several MODBUS transactions can be activated simultaneously on the same TCP
Connection.
Remark: If this is done then the MODBUS transaction identifier must be used to
uniquely identify the matching requests and responses
However you are sending messages with the transaction identifier set to 0000:
message[0] = 0;
message[1] = 0; //Message number
(I'm guessing that the gateway is detecting the duplication and assigning a new identifier because of this).
A better approach would be to send all of your requests (each with a different identifier; it does not matter what this is as long as it's unique) and then wait for the responses to come in (matching the response to the request using the transaction identifier in each response; responses may be in a different order to the requests).
Note that you should not assume that each response will come in a separate TCP packet (or that it will be in a single packet); the protocol takes this into account:
When MODBUS is carried over TCP, additional length information is
carried in the MBAP header to allow the recipient to recognize message
boundaries even if the message has been split into multiple packets for
transmission. The existence of explicit and implicit length rules, and use of a CRC-32 error check code (on Ethernet) results in an infinitesimal chance of undetected corruption to a request or response message.

Related

UDP Client - Reception of enqueued packets

I am developing a UDP Client PC application. It is supposed to receive UDP datagrams from more than 4 devices.
The system behaves in the following way:
Multiple devices are communicating with each other via UDP broadcasts on a fixed port (11000) forming a personal area network with no connectivity to the internet.
PC application is executed on a computer connected to the same network.
PC application listens to UDP broadcasts on 11000 port to discover the devices.
When a specific command is received from the PC application, that device goes into a different execution mode while other devices continue to broadcast their packets.
This behaves in the desired manner when there is only one device in the personal area network.
I am facing a strange issue when there are two or more devices in the network, such that:
I set the endPoint to the desired IPAddress and Port of the desired device using the discovered device list.
I call myUDP.Receive(ref endPoint); to receive UDP Datagram
This returns with the Datagram which was broadcasted by the second device in the network, rather than returning the response from the device with which I am trying to communicate. I have verified using the Wireshark that the response is sent from the device.
I tried looping through for a finite number of times to get the desired datagram.
// Some code which initializes the endPoint with desired IP Address and Port
...
// Some code which sends the data
...
// Some code which sets the IP Address of the device from which the response is expected
selectedIPAddress = IPAddress.Parse(labelIPAddressSettings.Text.Trim());
copyendPoint = endPoint;
// Listen to response
do
{
rexdDatagram = myUDP.Receive(ref endPoint);
if (endPoint.Address != selectedIPAddress)
{
// This datagram is not from the desired device
// Restore to the desired endpoint
endPoint = copyendPoint;
// Not sure if there is way to discard this enqueued datagram
}
i_timeout = i_timeout + 1;
if (i_timeout == 10)
{
// Datagram from the desired device has not been received
break;
}
// Not sure if the thread needs to sleep, debugging..
Thread.Sleep(1000);
} while (1);
Question:
Is my code correct to loop within enqueued datagrams? Is there a way to discard previous datagrams and start afresh?
The parameter remoteEP on the method UdpClient.Receive is not meant for specifying from which remote endpoint to receive from, but rather to specify which remote endpoint sent the data. You cannot selectively receive only from a specific endpoint.
Instead, you'll have to receive everything from everyone, and discard the packages that were not sent from your desired remote endpoint. You can do this like so:
byte[] receivedData = null;
var attempts = 0;
while (attempts < 10)
{
var recvEp = new IPEndPoint(IPAddress.Any, 0);
readData = myUDP.Receive(ref recvEp);
if (recvEp.Address == selectedIPAddress)
{
// We received data from the correct remote source
receivedData = readData;
break;
}
attempts++;
}
This code will receive data from anywhere, and if it doesn't receive data from the correct endpoint within 10 attempts, it will stop. Resulting in receivedData being null.
You might want to convert your code to wait for a certain amount of time not a certain amount of attempts, to increase the chances of actually receiving something. This could be done like so:
var start = DateTime.Now;
byte[] receivedData = null;
while((DateTime.Now - start).TotalSeconds < 10)
{
var recvEp = new IPEndPoint(IPAddress.Any, 0);
readData = myUDP.Receive(ref recvEp);
if (recvEp.Address == selectedIPAddress)
{
// We received data from the correct remote source
receivedData = readData;
break;
}
}
This code will try for 10 seconds, and stop after 10 seconds if nothing was received. This is not perfectly clean code, for example if you want to you can make this whole thing async.
Note: It is possible that both code snippets will result in an infinite loop, as myUDP.Receive(ref recvEp) will block as long as there isn't any incoming data. So if all your remote endpoints decide to stop sending data at the same time, the receive call will never return

Cheapest way to send small strings of data (30~40 bytes) over the internet?

I need to send small strings o data every minute or so from 100 or so android cellphones to some kind of server. The problem is that each MB i use is around 1.5 dollars, so the cost scales greatly if the data size is too big.
I have tried using post, but it used 400 bytes of data per string sent. I have tried to make a C# socket server where a client connects, sends the data and disconnects, but it still used 400 bytes of data, perhaps a bit more...how is this possible? Could netbalancer be measuring it wrong? I used client.Send("string") in c#.
Will it be any different if i do it from a mobile data cellphone? Im doing it to another laptop in a LAN.
I have also tried ftp and it was too bloated too.
I need the network consumption of each string sent be around 100-120 bytes or so, is this even possible? What tools could i use?
---Update---
Here is the C# client code (will be Java if I find out how to optimize the size)
// Read the first batch of the TcpServer response bytes.
Int32 port = 10000;
byte[] bytes = Encoding.ASCII.GetBytes("0001#37.12489#-106.35871");
TcpClient client = new TcpClient("192.168.15.16", port);
int bytesSent = client.Client.Send(bytes);
client.Close();
Here is the PHP socket running on XAMPP. I could make it in any language, as I am probably going to have to use it as a relay to a server that doesn't allow sockets.
<?php
error_reporting(E_ALL);
/* Allow the script to hang around waiting for connections. */
set_time_limit(0);
/* Turn on implicit output flushing so we see what we're getting
* as it comes in. */
ob_implicit_flush();
$address = 'xxx.xxx.xx.xx';
$port = 10000;
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false)
{
echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
}
if (socket_bind($sock, $address, $port) === false)
{
echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}
if (socket_listen($sock, 5) === false)
{
echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}
do
{
if (($msgsock = socket_accept($sock)) === false)
{
echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
break;
}
$buf = socket_read($msgsock,50);
socket_close($msgsock);
echo "$buf\n";
} while (true);
socket_close($sock);
?>
Do not send character strings, send binary data (preferable in network-byte-order). Sending the info in binary format reduces the size of the pay-load.
Go for a UDP connection, as this reduces protocol overhead.
Invent a minimal protocol to detect packet loss and allow requering of packets.
An idea:
Add a serial number to each packet. Keep a minimum number of packets sent on the sender side. Allow the sender to receive answers from the receiver. Let the receiver send a request to have the sender resend any serial number missing.
This would allow to fill gaps, which were introduced during a time window defined by the amount of packets kept by the sender.
For the serial number one addtional byte per packet might do, two bytes most certainly will be enough. However the maximum serial number (and with this its size) depends on the maxium number of packets the sender holds as history to be able to resend them.

TCP socket.receive() seems to drop packets

I am working on client-server appliction in C#. The comunication between them is with TCP sockets. The server listen on specific port for income clients connection. After a new client arrived, his socket being saved in a socket list. I define every new client socket with receive timeout of 1 ms. To receive from the client sockets without blocking my server I use the threadpool like this:
private void CheckForData(object clientSocket)
{
Socket client = (Socket)clientSocket;
byte[] data = new byte[client.ReceiveBufferSize];
try
{
int dataLength = client.Receive(data);
if (dataLength == 0)// means client disconnected
{
throw (new SocketException(10054));
}
else if (DataReceivedEvent != null)
{
string RemoteIP = ((IPEndPoint)client.RemoteEndPoint).Address.ToString();
int RemotePort = ((IPEndPoint)client.RemoteEndPoint).Port;
Console.WriteLine("SERVER GOT NEW MSG!");
DataReceivedEvent(data, new IPEndPoint(IPAddress.Parse(RemoteIP), RemotePort));
}
ThreadPool.QueueUserWorkItem(new WaitCallback(CheckForData), client);
}
catch (SocketException e)
{
if (e.ErrorCode == 10060)//recieve timeout
{
ThreadPool.QueueUserWorkItem(new WaitCallback(CheckForData), client);
}
else if(e.ErrorCode==10054)//client disconnected
{
if (ConnectionLostEvent != null)
{
ConnectionLostEvent(((IPEndPoint)client.RemoteEndPoint).Address.ToString());
DisconnectClient(((IPEndPoint)client.RemoteEndPoint).Address.ToString());
Console.WriteLine("client forcibly disconected");
}
}
}
}
My problem is when sometimes the client send 2 messages one after another, the server doesn't receive the second message. I checked with wireshark and it shows that both of the messages were received and also got ACK.
I can force this problem to occur when I am putting break point here:
if (e.ErrorCode == 10060)//recieve timeout
{
ThreadPool.QueueUserWorkItem(new WaitCallback(CheckForData), client);
}
Then send the two messages from the client, then releasing the breakpoint.
Does anyone met this problem before?
my problem is when sometimes the client send 2 messages one after another, the server doesn't receive the second message
I think it's much more likely that it does receive the second message, but in a single Receive call.
Don't forget that TCP is a stream protocol - just because the data is broken into packets at a lower level doesn't mean that one "send" corresponds to one "receive". (Multiple packets may be sent due to a single Send call, or multiple Send calls may be coalesced into a single packet, etc.)
It's generally easier to use something like TcpClient and treat its NetworkStream as a stream. If you want to layer "messages" on top of TCP, you need to do so yourself - for example, prefixing each message with its size in bytes, so that you know when you've finished receiving one message and can start on the next. If you want to handle this asynchronously, I'd suggest sing C# 5 and async/await if you possibly can. It'll be simpler than dealing with the thread pool explicitly.
Message framing is what you need to do. Here: http://blog.stephencleary.com/2009/04/message-framing.html
if you are new to socket programming, I recommend reading these FAQs http://blog.stephencleary.com/2009/04/tcpip-net-sockets-faq.html

How can I read from a socket repeatedly?

To start I am coding in C#. I am writing data of varying sizes to a device through a socket. After writing the data I want to read from the socket because the device will write back an error code/completion message once it has finished processing all of the data. Currently I have something like this:
byte[] resultErrorCode = new byte[1];
resultErrorCode[0] = 255;
while (resultErrorCode[0] == 255)
{
try
{
ReadFromSocket(ref resultErrorCode);
}
catch (Exception)
{
}
}
Console.WriteLine(ErrorList[resultErrorCode[0] - 48]);
I use ReadFromSocket in other places, so I know that it is working correctly. What ends up happening is that the port I am connecting from (on my machine) changes to random ports. I think that this causes the firmware on the other side to have a bad connection. So when I write data on the other side, it tries to write data to the original port that I connected through, but after trying to read several times, the connection port changes on my side.
How can I read from the socket continuously until I receive a completion command? If I know that something is wrong with the loop because for my smallest test file it takes 1 min and 13 seconds pretty consistently. I have tested the code by removing the loop and putting the code to sleep for 1 min and 15 seconds. When it resumes, it successfully reads the completion command that I am expecting. Does anyone have any advice?
What you should have is a separate thread which will act like a driver of your external hardware. This thread will receive all data, parse it and transmit the appropriate messages to the rest of your application. This portion of code will give you an idea of how receive and parse data from your hardware.
public void ContinuousReceive(){
byte[] buffer = new byte[1024];
bool terminationCodeReceived = false;
while(!terminationCodeReceived){
try{
if(server.Receive(buffer)>0){
// We got something
// Parse the received data and check if the termination code
// is received or not
}
}catch (SocketException e){
Console.WriteLine("Oops! Something bad happened:" + e.Message);
}
}
}
Notes:
If you want to open a specific port on your machine (some external hardware are configured to talk to a predefined port) then you should specify that when you create your socket
Never close your socket until you want to stop your application or the external hardware API requires that. Keeping your socket open will resolve the random port change
using Thread.Sleep when dealing with external hardware is not a good idea. When possible, you should either use events (in case of RS232 connections) or blocking calls on separate threads as it is the case in the code above.

C# Socket.SendTo in a loop eventually causes SocketException (depends on the router)

I am doing some basic Socket messaging. I have a routine that works well but there is a problem under load.
I'm using UDP to do a connectionless SendTo to basically do a ping-like operation to see if any of my listeners are out there on the LAN. Ideally I would just use the broadcast address, but Wireless routers don't seem to relay my broadcast. My work around is to iterate through all IPs on the Subnet and send my data gram to each IP. The other PCs are listening and if they get the message they will reply and that is how I get Peers to find each other. Here is the code that is in the loop which sends the data gram to each IP in the subnet.
string msgStr = "some message here...";
byte[] sendbuf = Encoding.ASCII.GetBytes(msgStr);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Blocking = true;
socket.SendTo(sendbuf, remoteEndPt);
//socket.Close();
This works, but when the Subnet range is large, say 255.255.0.0 (meaning ~60,000 IPs to iterate through) I will eventually get a SocketException with error code "10022", meaning "Invalid Argument". This tends to happen after ~10,000 or so successful sends then I start to see this error. Also, the router I use at work handles it and is presumably a high powered router, but the cheap-o one in my lab is the one that produces the error.
If I put in a wait time after catching the SocketException and before resuming the loop it will typically recover but eventually I'll get the error again.
I think what is happening is that the buffer on the router gets full and I cannot send anymore data. The higher quality one at work can handle it but the cheap-o one gets bogged down. Does that sound plausible?
A couple questions:
1) When using SendTo in a connectionless manner, do I need to call Close() on my Socket?
I've haven't seen any benefit in calling Close(), but when I do call Close() it severely slows down my iteration (I have it commented out above because it does slow things down a lot). Does this make sense?
2) Is there a way for me to tell I should wait before trying to send more data? It doesn't seem right to just catch the Exception which I still don't know what the cause of it is.
Thanks, J.
I am not sure that is the router only but I suspect that you are also running into some limit in the OS...
Any reason you are creating the Socket every time you send ?
Just reuse it...
Anyways according to http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.aspx it is a good idea to call Shutdown() and then Close() on the Socket... perhaps not with every send but every 255 IPs or so...
Checkout UdpClient - that could make implementation easier / more robust
EDIT - as per comment:
IF you want a Socket reuse "cache"... this for example would make sure that a specific Socket is only used every 256 checks...
// build/fill your Socket-Queue for example in the con
class SocketExample
{
Queue<Socket> a = new Queue<Socket>();
SocketExample ()
{
int ii = 0, C = 256;
for (ii = 0; ii < C; C++)
{
a.Enqueue (new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp));
}
}
// in your function you just dequeue a Socket and use it,
// after you are finished you enqueue it
void CheckNetIP (some parameters...)
{
Socket S = a.Dequeue();
// do whatever you want to do...
// IF there is no exception
a.Enqueue(S);
}
}

Categories

Resources