Network events synchronization problem

Hi,

I’m using serverEvent, clientEvent and disconnectEvent events to make a basic HTTP server. There seems to be synchronization problems between each events. Here’s my code:

import processing.net.*;

byte[][] Files = new byte[4][];
int HTTPPort = 80;
Server HTTPServer;
ClientInfo[] HTTPClients = new ClientInfo[0];

void setup()
{
  fullScreen();
  
  if((Files[0] = loadBytes("NotFound.html")) == null || (Files[1] = loadBytes("Page.html")) == null)
  {
    exit();
  }
  else
  {
    HTTPServer = new Server(this, HTTPPort);
  }
}

void draw()
{
  saveFrame("Frame.png");
  Files[3] = loadBytes("Frame.png");
  delay(3000);
  background(random(255), random(255), random(255));
}

void serverEvent(Server CurrentServer, Client NewClient)
{
  print("New HTTP Client.\r\n");
  HTTPClients = (ClientInfo[])append(HTTPClients, new ClientInfo(CurrentServer, NewClient));
}

void clientEvent(Client Client)
{
  int i;
  String Buffer;
  
  for(i = 0; Client != HTTPClients[i].GetClient(); i += 1);
  
  while((Buffer = HTTPClients[i].GetClient().readStringUntil('\n')) != null)
  {
    if(Buffer.startsWith("GET /"))
    {
      HTTPClients[i].SetFile(splitTokens(Buffer, " ?")[1]);
    }
    else if(Buffer.equals("\r\n"))
    {
      print("HTTP Request Received.\r\n");
      HTTPClients[i].GetClient().write("HTTP/1.1 200 OK\r\n\r\n");
      if(HTTPClients[i].GetFile().equals("/"))
      {
        HTTPClients[i].GetClient().write(Files[1]);
      }
      else if(HTTPClients[i].GetFile().equals("/favicon.ico"))
      {
        HTTPClients[i].GetClient().write(Files[3]);
      }
      else if(HTTPClients[i].GetFile().equals("/Frame.png"))
      {
        HTTPClients[i].GetClient().write(Files[3]);
      }
      else
      {
        HTTPClients[i].GetClient().write(Files[0]);
      }
      print("HTTP Response Sent.\r\n");
      
      print("Disconnecting HTTP Client.\r\n");
      HTTPClients[i].GetClient().stop();
      break;
    }
  }
}

void disconnectEvent(Client OldClient)
{
  int i;
  
  for(i = 0; OldClient != HTTPClients[i].GetClient(); i += 1);
  for(; i < HTTPClients.length - 1; i += 1)
  {
    HTTPClients[i] = HTTPClients[i + 1];
  }
  HTTPClients = (ClientInfo[])shorten(HTTPClients);
  print("test\r\n");
}

class ClientInfo
{
  int Time;
  Server Server;
  Client Client;
  String File;
  ClientInfo (Server CurrentServer, Client NewClient)
  {
    Time = millis();
    Server = CurrentServer;
    Client = NewClient;
    File = "";
  }
  Server GetServer()
  {
    return Server;
  }
  Client GetClient()
  {
    return Client;
  }
  void SetFile(String NewFile)
  {
    File = NewFile;
  }
  String GetFile()
  {
    return File;
  }
}

Sometime I get an array index out of bounds (java.lang.ArrayIndexOutOfBoundsException: 0) at line 46. It look like clientEvent is called before serverEvent when a new client connect and send data. I think that’s what happening because the index goes out of bounds only if the Client parameter of clientEvent is not found in the HTTPClients global array (see line 46). The serverEvent, supposed to be called when a new client connect, is responsible for adding new clients in the HTTPClients global array. I’m not sure how to fix this problem, I’m new to Java (I’m used to program in C), any help would be appreciated.

Thanks!

I found the problem the File string in the ClientInfo class is not initialized. I initialized it to “” in the class constructor.

Edit:

This definitely did something because the error occur less often but it still occur from time to time. Actually I’m not quite sure if the problem is caused by what I initially described the only thing I know is that the error occur in clientEvent.

Is this sketch only for your server and do you have another sketch for your client? Can you provide steps to reproduce the problem?

Let us know if your issue still persist.

One thing: I encourage you to write code for readability over performance (at least at this point). Calling this HTTPClients[i].GetClient().write(Files[0]); to return NotFound.html is just obscure.

In addition, for(i = 0; Client != HTTPClients[i].GetClient(); i += 1); may look like clever but I would prefer to write int i=HTTPClients.length-1; instead. Finally, you need to keep in mind that you have a Client object in your server code that you should be using: void clientEvent(Client Client). Explore the documentation to clarify the usage.

Kf

This sketch is only for the server, I do not have another sketch for the client. Since it’s an HTTP server the client is a web browser. I personally use Google Chrome as the client.

To reproduce the problem you will first need to add the following files in the Processing project folder:

Page.html

<!DOCTYPE html>
<html>
<head>

<script src="https://code.jquery.com/jquery-3.5.0.js"></script>

</head>
<body>

<img id="Frame">

<script>

d = new Date();
setInterval(function(){$("#Frame").attr("src", "Frame.png?" + d.getTime());}, 500)

</script>
</body>
</html>

NotFound.html

<!DOCTYPE html>
<html>
<body>

<h1>404 Not Found</h1>

</body>
</html>

Then you can follow these steps:

  1. Run the Processing code.
  2. Retrieve your computer local IP address (ipconfig, ifconfig, router webpage (DHCP table), …).
  3. Enter that IP address as a URL in your web browser and press enter.

You will need to disable caching on your browser otherwise your browser will likely cache Frame.png (I need to add some fields in the HTTP header to fix this). One way to do it, on Google Chrome, without changing your browser settings is by doing the following:

Right click anywhere on the webpage and click Inspect:
image

Go to the Network tab:
image

Check Disable cache:
image

You may need to refresh the webpage. Caching will now be disable for this browser tab only. It will remain disabled until you close the Inspect window or close the tab.

You should see the Processing canvas in your browser. The Processing canvas is simply a background that change color every 3 seconds. The problem will occur after few seconds or minutes. You should see an error message in the Processing console.

I can’t write int i=HTTPClients.length-1; since the new data received may not be from the last client who connected to the server if there is more than one client connected. The Client object in the server code void clientEvent(Client Client) should be the same as HTTPClients[i].GetClient() since i value is set by doing for(i = 0; Client != HTTPClients[i].GetClient(); i += 1);.

Edit:
The reason why I’m using HTTPClients[i] in void clientEvent(Client Client) is because I will have another server running on a different port in the same code. I will then be able to know the client is connected to which server using HTTPClients[i].GetServer() since void clientEvent(Client Client) doesn’t have a Server object parameter.