Multiple connections to an Ethernet shield

I have an application where we have built several ModBus slaves using Arduino Megas with Wiznet's ethernet module. They work fine although they do lock up from time to time. My question though has to do with multiple clients connecting to the board at the same time and sending essentially the same commands.

How does the Ethernet Shield know when one command is finished (to one client) and the next one begins? If two are sent one after the other, what is my "client.available()" going to tell me? Will it be the total number of bytes from BOTH clients or just the first one? If it's just the first one, when my loop comes around again and does "client = server.available()" again, will it then give me the other packet (that was sent from a different machine)?

Is there anyway to gain access to the tcp/ip info (like source ip/port number) from within the current library?

Thanks for any info you can provide.

len morgan Colorado River Municipal Water District

The client.available() call will return the number of characters currently in the receive buffer for that socket. Each connection will get its own socket, so it only shows the number of characters for each connection request.

You might want to identify each client by either the page the client requests or variables sent by the client.

When you are talking about a "socket" here, are you saying that the return value from "server.available()" is a socket (rather than a Client object)? That would clear things up a little for me if that were the case.

Also, the clients are not visiting "pages" or sending variables. This is a ModBus/TCP conversation(s) so I don't have access to who sent the request or issued the command. That is the second part of my original post: Is the source IP and/or port number available from the library anywhere? I get back a Client object (which appears to be a socket #) when the connection is opened, but I haven't seen a function (like client.ipsourceaddress() or client.portnumber()) in the library. If I wanted this information, would I need to modify the library to create these functions?

Thanks,

Len Morgan

My question though has to do with multiple clients connecting to the board at the same time and sending essentially the same commands.

Below is some meta refresh code that you can test multiple browser client connections. You can change the meta refresh value from 2 to 0 to attempt the fastest connections to the server, and open multiple browser instances of the page to see how well they compete for the server data updates. There is also a client.getRemoteIP(rip); function available that can get the ip address of the client being served.

// zoomkat meta refresh server test code
// arduino IDE 1.0
// for W5100 ethernet shield
// the IP address will be dependent on your local network/router
// port 80 is default for HTTP, but can be changed as needed
// use IP address like http://192.168.1.102:84 in your brouser
// or http://zoomkat.no-ip.com:84 with dynamic IP service
// use the \ slash to escape the " in the html
// meta refresh set for 2 seconds

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

int x=0; //set refresh counter to 0
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,1,102); // ip in lan
EthernetServer server(84); //server is using port 84

void setup()
{
  // start the server
  Ethernet.begin(mac, ip);
  server.begin();
}

void loop()
{
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
     while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        // see if HTTP request has ended with blank line
        if (c == '\n') {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();
          
          //meta-refresh page every 2 seconds
          x=x+1; //page upload counter
          client.println("<HTML>");
          client.print("<HEAD>");
          client.print("<meta http-equiv=\"refresh\" content=\"2\">");
          client.print("<TITLE />Zoomkat's meta-refresh test</title>");
          client.print("</head>");
          client.println("<BODY>");
          client.print("Zoomkat's meta-refresh test IDE 1.0");
          client.println("
");
                    
          client.print("page refresh number ");
          client.println(x); //current refresh count
          client.println("
");
          client.println("
");
          
          client.print("Zoomkat's arduino analog input values:");
          client.println("
");
          client.println("
");
          
          // output the value of each analog input pin
          for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
            client.print("analog input ");
            client.print(analogChannel);
            client.print(" is ");
            client.print(analogRead(analogChannel));
            client.println("
");
            }
           break;
          client.println("</BODY>");
          client.println("</HTML>");
         }
        }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
  }
}

I get back a Client object (which appears to be a socket #) when the connection is opened, but I haven't seen a function (like client.ipsourceaddress() or client.portnumber()) in the library. If I wanted this information, would I need to modify the library to create these functions?

Unfortunately, you need to modify the library to get that data. :(

best I can tell from going through the source is there is no direct api available to determine the ip address of the cilent as that is hidden by the wiznet chip and the only interface to arduino is via SPI and you only get one of 4 sockets the server is connected to and if there is data available in the receive buffer.

you can try adding this to EthernetClient.cpp (and the corresponding entry in .h)

uint32_t EthernetClient::destIp() {
  byte b[4];
  if (_sock != MAX_SOCK_NUM)
    return IPAddress(W5100.readSnDIPR(_sock, b));
  return 0;    
}

and add this to EthernetClient.h

uint32_t destIp();

and in your sketch do

  IPAddress addr = client.destIp();

where client is the client you got from server.available()

I have not tested if the above code will work.

I did a quick test and it did not work. It always return 4. So either I called the function incorrectly or the register is write only.

actually, it works. The ip address is in the byte array.

here is the code that works.

insert these at line 15 of EthernetClient.h

  IPAddress destIp();
  uint16_t destPort();

insert these at line 23 of EthernetClient.cpp

IPAddress EthernetClient::destIp() {
  byte b[4] = {0,0,0,0};
  if (_sock != MAX_SOCK_NUM) {
    W5100.readSnDIPR(_sock, b);
  }
  return IPAddress(b);
}

uint16_t EthernetClient::destPort() {
  if (_sock != MAX_SOCK_NUM) {
    return W5100.readSnDPORT(_sock);
  }
  return 0;  
}

To test these, you can use webserver sample, and add these lines after server.available() is true

        IPAddress addr = client.destIp();
        Serial.print("Client IP address: ");
        Serial.println(addr);
        uint16_t port = client.destPort();
        Serial.print("Client port is ");
        Serial.println(port);

I am attaching the files as well. Just backup and replace the files in Ethernet libraries folder.

EthernetClient.cpp (3.54 KB)

EthernetClient.h (845 Bytes)

Some server test code that captures the client ip address.

//zoomkat 12-8-11
//simple button GET with iframe code
//for use with IDE 1.0
//open serial monitor to see what the arduino receives
//use the \ slash to escape the " in the html 
//address will look like http://192.168.1.102:84 when submited
//for use with W5100 based ethernet shields

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

byte rip[4];
//byte rip[] = {0,0,0,0};
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //physical mac address
byte ip[] = { 192, 168, 1, 102 }; // ip in lan
byte gateway[] = { 192, 168, 1, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
EthernetServer server(84); //server port

String readString; 

//////////////////////

void setup(){

  pinMode(4, OUTPUT); //pin selected to control
  //start Ethernet
  Ethernet.begin(mac, ip, gateway, subnet);
  server.begin();

  //enable serial data print 
  Serial.begin(9600); 
  Serial.println("server LED test 1.0"); // so I can keep track of what is loaded
}

void loop(){
  // Create a client connection
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        //read char by char HTTP request
        if (readString.length() < 100) {

          //store characters to string 
          readString += c; 
          //Serial.print(c);
        } 

        //if HTTP request has ended
        if (c == '\n') {

          ///////////////
          Serial.println(readString); //print to serial monitor for debuging 
client.getRemoteIP(rip);
for (int bcount= 0; bcount < 4; bcount++)
     { 
        Serial.print(rip[bcount], DEC); 
        if (bcount<3) Serial.print(".");
     } 

Serial.println();
          //now output HTML data header
             if(readString.indexOf('?') >=0) { //don't send new page
               client.println("HTTP/1.1 204 Zoomkat");
               client.println();
               client.println();  
             }
             else {
          client.println("HTTP/1.1 200 OK"); //send new page
          client.println("Content-Type: text/html");
          client.println();

          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<TITLE>Arduino GET test page</TITLE>");
          client.println("</HEAD>");
          client.println("<BODY>");

          client.println("<H1>Zoomkat's simple Arduino button</H1>");
          
          client.println("<a href=\"/?on\" target=\"inlineframe\">ON</a>"); 
          client.println("<a href=\"/?off\" target=\"inlineframe\">OFF</a>"); 

          //client.println("<IFRAME name=inlineframe src=\"res://D:/WINDOWS/dnserror.htm\" width=1 height=1\">");
          client.println("<IFRAME name=inlineframe style=\"display:none\" >");          
          client.println("</IFRAME>");

          client.println("</BODY>");
          client.println("</HTML>");
             }

          delay(1);
          //stopping client
          client.stop();

          ///////////////////// control arduino pin
          if(readString.indexOf("on") >0)//checks for on
          {
            digitalWrite(4, HIGH);    // set pin 4 high
            Serial.println("Led On");
          }
          if(readString.indexOf("off") >0)//checks for off
          {
            digitalWrite(4, LOW);    // set pin 4 low
            Serial.println("Led Off");
          }
          //clearing string for next read
          readString="";

        }
      }
    }
  }
}