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();
}
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
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().
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.
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."
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.
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