Ethernet Shield - Two steps communication protocol

I managed to let it works. Not exactly as wanted, but it works.

Here is the arduino sketch:

#include <SPI.h>
#include <Ethernet.h>

byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 33, 17 };
byte gateway[] = { 192, 168, 33, 13 };
byte server[] = { 192, 168, 33, 16 };  // notebook

Client client(server, 8001);

void setup() {
  Ethernet.begin(mac, ip, gateway);
  Serial.begin(9600);
  
  // give the Ethernet shield a second to initialize
  delay(1000);
  Serial.println("connecting...");
  
  byte *buffer = NULL;
  byte buffer_h[] = "My client header...";
  byte buffer_m[] = "My client payload, too!!! :))";
  byte *h = NULL, *p = NULL;

  if (client.connect()) {
    Serial.println("connected");
    Serial.println("- Sending message header -");
    sendMessageHeader(buffer_h);
    Serial.println("- Sending message payload -");
    sendMessagePayload(buffer_m);
    
    Serial.println("- Reading server response -");
    // fundamental delay to receive the server message
    delay(1500);
    buffer = readMessageFromServer();
    Serial.println();
    Serial.println("- Parsing server response -");
    parseServerResponse(buffer, h, p);
  } 
  else {
    Serial.println("connection failed");
  }
}

void loop()
{
  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();

    for(;;)
      ;
  }
}

void sendMessageHeader(byte *buf) {
  client.println((char *) buf);
}

void sendMessagePayload(byte *buf) {
  client.println((char *) buf);
}

byte * readMessageFromServer() {
  
  byte buf[50];
  int i = 0;
  
  while (client.available()) {
    char c = client.read();
    Serial.print(c);
    buf[i] = c;
    i++;
  }
  return buf;
}

void parseServerResponse(byte *buf, byte *head, byte *payload) {
  head = readResponseHeader(buf);
  payload = readResponsePayload(buf);
}

byte * readResponseHeader(byte *buf) {
  byte head[20];
  
  for (int i = 0; i < 20; i++) {
    head[i] = buf[i];
  }
  return head;
}

byte * readResponsePayload(byte *buf) {
  int offset = 20;
  byte payload[30];
  
  for (int i = offset; i < 30 + offset; i++) {
    payload[i] = buf[i];
  }
  return payload;
}

And here is the C server (for brevity, only essential lines are reported:

     memset(buffer, 0, 51);
     n = read(newsockfd, buffer, 20);

     printf("Header received: %s\n", buffer);

     memset(buffer, 0, 51);
     sleep(1);  // without this, payload is not readed correctly
     n = read(newsockfd, buffer, 30);

     printf("Payload received: %s\n", buffer);

     // writing in a single step
     memset(buffer, 0, 51);
     strncpy(buffer, "Header is now ready\nAnd payload is ready too...", 50);
     n = write(newsockfd, buffer, strlen(buffer));

Issues:

  • it only works with delays (both client and server side)
  • I cannot send header and payload separately to the client. I must use a single write() call

I managed to let it works. Not exactly as wanted, but it works.

    sendMessageHeader(buffer_h);
    sendMessagePayload(buffer_m);

I cannot send header and payload separately to the client. I must use a single write() call

I guess you have a different definition of 1 than the rest of us.

it only works with delays (both client and server side)

There is no need for the delay on the client side.

You have a fundamental misunderstanding of header/payload. If you are really sending a header and payload separately, the header would define the size of the payload.

  byte buffer_h[] = "My client header...";

How is the server supposed to know the size of the payload from this crap?

The delay on the client side is there to give the server time to respond. It is currently needed because the readMessageFromServer method is not waiting for some number of bytes or some specific end of packet marker.

Try again.

I have no formal programming training, but I am not a complete novice programmer, but if I am completely wrong, just ignore this post.

Why don't you implement some logic that uses something other than delays? It would take a bit more code, but I would use (and am using), while loops that wait for confirmation of data received. (client sends data and server sends confirmation) This provides a built-in pseudo delay. (of course you would also want some sort of TTL so it does not infinitely loop.)

Perhaps this was your issue with being unable to send your header and payload separately from the server, c was too fast and got both writes to the serial port before the arduino could read.

For example, as a test in PHP I printed to the screen whenever I wrote and whenever I read and implemented confirmations. My arduino sketch would simply be...read, if the read == 1 write 1. PHP below. I would get on average 2-3 writes to 1 read. I am also using serial/usb connection, so I have no idea on the lag using Ethernet, could be even longer. Perhaps when I get time I will send timestamp to see the actual delay.

while (TRUE) {
  fwrite(1);
  echo 'wrote 1';
  $x = fread();
  if ($x == 1) {
    echo 'read 1';
    break;
  }
}