SOLVED: Ethernet client connection fails after many client prints

Hello,

I have a question regarding the Ethernet library which has been frustrating me for the last few days. I've has a quick look at the forum, but can't seem to find anything similar.

After sending many HTTP requests, client.println(...), the client starts to fail when connecting. This happens roughly after a few thousand HTTP messages have been sent. The messages are very small, and I don't believe it is a problems with the transmit buffer being full (I followed the advice in this thread - http://bit.ly/pOxkcQ)

Here is the code that is failing. Its rather short and simple:

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

// Network constants
byte mac[] = {0x00, 0x23, 0xdf, 0x82, 0xd4, 0x01};
byte ip[] = {/*REDACTED*/};
byte server[] = {/*REDACTED*/};
int port = /*REDACTED*/;

// State
int sequence;

void setup(){
  Ethernet.begin(mac, ip);
  Serial.begin(9600);
  sequence = 0;
  
  delay(1000);
}

void loop(){
  httpPut("/topic/:test/publish?sessionId=SESenanhygrp");
  Serial.println(sequence++);
}

void httpPut(char* url){
  Client client(server, port);
  
  if (!client.connect()) {
    Serial.println("EXCEPTION: during HTTP PUT. Could not connect");
    return;
  }
  
  client.print("PUT");
  client.print(" ");
  client.print(url);
  client.println(" HTTP/1.0");
  client.println();
  
  while(client.available()) {
    client.read(); 
  }
  
  client.stop();
}

Thanks in advance!
Chris

I put this up with the data statements, not in the loop.

  Client client(server, port);

I would have put it right under
int port

Ah yes, thank you SurferTim. Don't know why I put it in the loop (I've always placed it with the data statements in the past)!

I've quickly tested it again, but unfortunately the problem still remains...

Do you know where in the loop it is failing? I would put Serial.begin(9600) and some Serial.println() calls to try to figure out where it is failing.

How long or how many connections causes it to fail? Close as you can guess.

Edit: I hope that is your server. The way I see that code, my server might interpret that as a DoS attack.
How about put a

delay(100);

in loop(). Keep that connection request to every tenth of a second.
Or wait until the server is finished sending.

And insure you have transmit buffer space before sending that many requests in a row.
Maybe this thread will help. You may be overflowing the transmit buffer on the w5100 IC.
http://arduino.cc/forum/index.php/topic,72027.15.html

I should have been more descriptive in my original post.

The time to failure appears random, and the sequence readout from the loop can vary anywhere between ~1000 and ~7000.
The specific error is in

  if (!client.connect()) {
    Serial.println("EXCEPTION: during HTTP PUT. Could not connect");
    return;
  }

It fails to connect, and prints EXCEPTION: during HTTP PUT. Could not connect continuously.

It is our development server on a private connection, and should not be interpreting it as a DoS attack.

With the 100ms delay, it still fails in the same manner (sequence = ~8592).

Out of interest, what do you mean by

Or wait until the server is finished sending.

I will do further tests to ensure the transmit buffer isn't filling up and report back.

Thanks again for your help! (Really appreciated)

Normally the server will terminate the connection when it is finished. It appears you are reading only what you have received so far before closing the connection and making another request.

I use a persistent connection with a Linux server. It does not terminate the connection unless it determines the server has terminated it.

My Arduino client initiates the connection, and sends the first "packet" to the server, waits 30ms, checks for client.available() for a response "packet", then sends another "packet" if there is room in the transmit buffer.

Every trip through the loop, it checks the connection

if(!client.connected() // reconnect

If not connected, it attempts to connect.

But I have this limited to 30 times a second with "delay(30);" in the loop().

I have tested some more, with the following changes to the code I originally posted

...

// State

int newFree;

...

void httpPut(char* url){
  if (!client.connect()) {
    Serial.println("EXCEPTION: during HTTP PUT. Could not connect");
    return;
  }
  
  checkEthernetBuffer("#After connection -\t");
  
  client.print("PUT");
  client.print(" ");
  client.print(url);
  client.println(" HTTP/1.0");
  client.println();
  
  checkEthernetBuffer("#After PUT request -\t");
  
  while(client.available()) {
    client.read(); 
  }
  
  checkEthernetBuffer("#After client read -\t");
  
  client.stop();
}

void checkEthernetBuffer(char* codeLocation){
  newFree = client.free();
  Serial.print(codeLocation);
  Serial.println(newFree, DEC);
}

The output throughout was

#After connection -	2048
#After PUT request -	2047
#After client read -	2048

Unless I have done a silly , it appears that the problem is not to do with the overflowing transmit buffer.

I gotta ask now because I just noticed. Why are you using PUT rather than GET?

Edit: That Serial output looks good. You are not overflowing the buffer.

SurferTim:
Normally the server will terminate the connection when it is finished. It appears you are reading only what you have received so far before closing the connection and making another request.

...

My Arduino client initiates the connection, and sends the first "packet" to the server, waits 30ms, checks for client.available() for a response "packet", then sends another "packet" if there is room in the transmit buffer.

Every trip through the loop, it checks the connection

if(!client.connected() // reconnect

If not connected, it attempts to connect.

Ah, so you suggest that I connect once during setup, and then just check if the arduino is still connected in every loop (if not, just reconnect)?

Am I also right in saying that

  while(client.available()) {
    client.read(); 
  }

Is the correct way of listening for a response, but I should probably wait ~30ms between my last client.print(...) and when i start listening?

Thanks again

EDIT: I'm using PUT as the application will need to send a JSON formatted string to the server. For testing purposes, PUTing a NULL strings seems appropriate...

That is what I do. I connect with the client to "Mom" (server). Every trip through the loop, I check to insure client.connected().
If not, then client.stop() delay(10) and client.connect().

I send the data to the server that I need to send this time through, then wait 30 ms, and available/receive. The send has a terminating byte that the server knows the client is done sending for now, like the two return characters in a row. When the server receives that, it sends its stuff to the client, then waits for another "packet" from the client. It also does not close the connection until it determines the connection is lost.

The server uses basically the same protocol. It also has a "done sending" like two return characters in a row.

Edit: This is not an Apache server I am talking about. The server I use is a custom server designed for this application. Apache responds and terminates the connection.

Thank again for your help.

I have tried 2 different approaches, both with no success:

  • I have tried just placing a 30ms delay between sending and awaiting a response, resulting in the response actually being received but the error still appears later on.

  • I have tried following your setup as close as possible, but client.connected() always seems to return false, resulting in reconnection within the httpPut(...) function. As usual, later on in the process, the client.connect() continuously fails.

The code for 2nd approach (attempting to follow your setup):

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

// Network constants
byte mac[] = {0x00, 0x23, 0xdf, 0x82, 0xd4, 0x01};
byte ip[] = {/*REDACTED*/};
byte server[] = {/*REDACTED*/};
int port = /*REDACTED*/;
Client client(server, port);

// State
int sequence;
char clientResponce;

void setup(){
  Ethernet.begin(mac, ip);
  Serial.begin(9600);
  sequence = 0;
  
  delay(1000);
  
  if (!client.connect()) {
    Serial.println("EXCEPTION: during setup. Could not connect");
    return;
  }
  
  Serial.println("Client connected");
}

void loop(){
  httpPut("/topic/:test/publish?sessionId=SESenanhygrp");
  Serial.println(sequence++);
}

void httpPut(char* url){
  
  // Checks for connection
  if (!client.connected()) {
    Serial.println("EXCEPTION: not connected. Attempting to reconnect");
    client.stop();
    
    delay(10);
    
    if (!client.connect()) {
      Serial.println("EXCEPTION: Could not connect!");
      return;
    }
  }
  
  client.print("PUT");
  client.print(" ");
  client.print(url);
  client.println(" HTTP/1.0");
  client.println();
  
  delay(30);
  
  while(client.available()) {
    clientResponce = client.read();
	// Serial.print(clientResponce);
  }
}

Thanks again

EDIT: I (think) I now understand that the problem* I mention in the second bullet point is not actually a problem. I believe it is the server disconnecting, having successfully sent a response following my HTTP request.

*"but client.connected() always seems to return false, resulting in reconnection within the httpPut(...) function."

What is the server? Your custom program? IIS? Apache?

The server application is written in Java and uses Jetty.

To test further, I placed some serial prints in the Client.cpp Client::connect() method

uint8_t Client::connect() {
  if (_sock != MAX_SOCK_NUM){
	Serial.println("EXCEPTION: Not all sockets are available");
    return 0;
  }

  for (int i = 0; i < MAX_SOCK_NUM; i++) {
    uint8_t s = W5100.readSnSR(i);
    if (s == SnSR::CLOSED || s == SnSR::FIN_WAIT) {
      _sock = i;
      break;
    }
  }

  if (_sock == MAX_SOCK_NUM){
  	Serial.println("EXCEPTION: No sockets were available!");
    return 0;
  }

  _srcport++;
  if (_srcport == 0) _srcport = 1024;
  socket(_sock, SnMR::TCP, _srcport, 0);

  if (!::connect(_sock, _ip, _port)) {
    _sock = MAX_SOCK_NUM;
	Serial.println("EXCEPTION: in Ethernet connect library");
    return 0;
  }

  while (status() != SnSR::ESTABLISHED) {
    delay(1);
    if (status() == SnSR::CLOSED) {
      _sock = MAX_SOCK_NUM;
	  Serial.println("EXCEPTION: Socket was closed after being established");
      return 0;
    }
  }

  return 1;
}

I've done some tests using both http put and get, on 2 different internal servers, all of which failed during connect at the start of the loop (this again occurs after a few thousand successful http requests...)

EXCEPTION: Socket was closed after being established
EXCEPTION: Could not connect!

EXCEPTION: No sockets were available!
EXCEPTION: Could not connect!

EXCEPTION: No sockets were available!
EXCEPTION: Could not connect!
...

Hopefully this may help identify what the problem may be.

Your help is really appreciated!

It appears your server is not finished sending when you try another connection, and the server still has the previous socket open. You can use 4 sockets in a hurry that way.

Does the server terminate the connection when it is finished sending a response? If it does, you should keep reading the response until you lose the connection.

My impression is that it was possible to ensure that you had closed the sockets. Unless I am wrong, the httpGet(...) function below would ensure the sockets are closed as after posting, it waits for a server response, then waits for the connection to terminate, then calls client.stop().

void httpGet(){
  // Connects to the server if possible 
  if (!client.connect()) {
    Serial.println("EXCEPTION: Could not connect!");
    return;
  }
  
  // Write to server
  client.println("GET / HTTP/1.0");
  client.println();
  
  // Waits for server response
  while(!client.available()){
   delay(1); 
  }
  
  // Read response from server
  while(client.available()) {
    char c = client.read();
    Serial.print(c);
  }
  
  // Wait for server to terminate
  while(client.connected()){
   Serial.println("Waiting for server to terminate"); 
  }
  
  // Disconnect
  client.stop();
}

I'm going to take a closer look at the server logs and run some tests to see if the sessions are closing properly.
Thanks again :slight_smile:

I don't use this type of repetitive connection. I open the connection and keep it open like a streaming video server does.

I think I would try this:

while(client.connected()){

  while(client.available()) {
    char c = client.read();
    Serial.print(c);
  }

}

So after more testing, finally found the solution! :slight_smile:

Someone suggested I try the Ethernet2 library (http://bit.ly/p8ODaV). This needed a little tweaking, but now everything is working absolutely fine!

I assume there must be a bug somewhere in the Ethernet library?

I assume there must be a bug somewhere in the Ethernet library?

Yes. In Arduino IDE V0022, Client::available() has a bug. For me, that is "had a bug". :slight_smile:

Here is the thread:
http://arduino.cc/forum/index.php/topic,68624.0.html

Ahhh, thanks for linking me to the thread (I can finally understand what has been giving me all these headaches :P)

BTW, does Arduino have a bug tracker, or a list of known issues somewhere?

Here are two main sites that maintain the Arduino code (that I know about anyway):

Arduino
http://code.google.com/p/arduino/
On the "issues" page, the ethernet library bug/fix report is #605

avr-libc

There may be one more that maintains the avr-gcc code, but I have yet to find it.