Ethernet client.connected() problema sui socket

Ciao.

Qualcuno che ha la wiznet W5500 potrebbe provarmi questo sketch scritto "alla veloce" per favore?

Il test consiste nel ripetere il programma facendo un reset o chiudendo ll serial monitor e riaprirlo per più di 4 volte in meno di 60 secondi, le risposte devono essere pressochè immediate e fornire esattamente il dato senza andare in timeout.

Il problema che si riscontra nel W5100 è che i socket interessati o il singolo socket resta freezzato al cod. 0x15 che sarebbe la condizione di ESTABLISHED come da datasheet, trascorso il timeout passa in 0x17 per poi andare in CLOSE a 0x00.
Sembrerebbe che la W5500 avendo una gestione diversa dei socket il software sia stato rifatto e rivisto e che questo non accada.

Il problema non è la mancata risposta del server remoto, ho già appurato che 50 richieste su 50 vengono soddisfatte tutte al 100% in meno di 60 sec da un altro client.
Se effettivamente il software del 5500 non lo fa, me lo vado a spulciare e cerco di adattarlo al W5100.

restando in attesa ... porgo distinti saluti

// 29/10/2016 IDE 1.6.12
#include <SPI.h>
#include <Ethernet.h>
//#include "utility/w5100.h"

//---------------------- ETHERNET variabili -------------------------------------------
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = {192,168,2,177}; //IP statico 
byte gateway[] = {0,0,0,0};
byte dnss[] = {0,0,0,0};
byte subnet[] = {0,0,0,0};

int serv_port = 80;
byte dhcp = 1; // 0 = IP statico ... 1 IP dinamico restituito da DHCP
EthernetServer server(serv_port);
//http://api.ipify.org //get ip pubblico
//------------------------------------------------------------------------------------- 

//---------------------- NPT variabili ------------------------------------------------
const unsigned long interval_c = 60000; // test ogni min
unsigned long previousMillis_c = 0;    //contatore millisecondi aggiornamento a NTP
IPAddress ip_ntp_1(193,204,114,232);
//------------------------------------------------------------------------------------- 

void setup(){
   delay(300);
   pinMode(4, OUTPUT); pinMode(10, OUTPUT);      
   digitalWrite(4, 1); digitalWrite(10, 1);
  
  Serial.begin(115200);
  Ip_Dhcp_set();

  delay(2);
  if(dhcp){
      Serial.println("DHCP: Enable");
      Ip_Dhcp_set();
   }   
  else Ip_Static_set();
  server.begin();

 //for (byte i = 0; i < 4; i++) { 
  //Serial.print(Ethernet.localIP()[i],DEC);
  //Serial.print("."); 
 //}
  
  Serial.print("IP: "); Serial.print(Ethernet.localIP());
  Serial.print(":"); Serial.println(serv_port);
  Serial.print("SUBNET: "); Serial.println(Ethernet.subnetMask());
  Serial.print("GATEWAY: "); Serial.println(Ethernet.gatewayIP());
  Serial.print("DNS SERVER: "); Serial.println(Ethernet.dnsServerIP());
  //Serial.print("NTP SERVER: "); Serial.print(ip_ntp_1);  Serial.println(":8888");
  Serial.println();
//--------------------------------------------------------------------
delay(5);
  EthernetClient client;
  Serial.println("Lettura IP Pubblico ATTENDERE ...");
  if (client.connect("api.ipify.org", 80)) {    
      client.write("GET / HTTP/1.1\r\n");
      client.write("Host: api.ipify.org\r\n");
      client.write("Connection: close\r\n");
      client.println();
        } else {
          Serial.print("Impossibile determinare IP Pubblico!");
          Serial.println(" >> ERRORE DI RETE!");
         }

   String readString;
   int16_t c;  
   while(client.connected()){
  
      if (client.available()) { 
        while ((c = client.read())>0){
           readString += (char)c;
             if((char)c=='\n') readString="";        
       }
    } 
  }
      delay(1);
      client.flush();
      client.stop(); 
   Serial.print("IP PUBBLICO: ");  
   Serial.println(readString);
   Serial.println();    
//--------------------------------------------------------------------
}
 
void loop(){

  if(millis() - previousMillis_c > interval_c) {   
        previousMillis_c = millis();    
        //debug socket #0 #1 #2 #3 
      }       
}

void Ip_Dhcp_set(){        
        Ethernet.begin(mac);
}

void Ip_Static_set(){
        Serial.println("DHCP: Disable");
        //Serial.print(Ethernet.localIP()[i],DEC);
        //ip[3] = Ethernet.localIP()[3],DEC;
        Ethernet.begin(mac, ip);  
}

Allora,
ho modificato le seguenti due righe :

#include <Ethernet2.h>
#include "utility/w5500.h"

Compilato, lanciato, ha dato l'IP pubblico, chuso il monitor seriale, riaperto ... "Impossibile determinare IP Pubblico! >> ERRORE DI RETE!"

... direi che c'è qualche problemino ::slight_smile:

Guglielmo

Grazie gpb

Ma neanche una volta?
La prima deve dartelo, almeno a me lo da, dalla seconda in poi fallisce... dopo circa 30-60 sec (non ho preso il tempo esatto), allora funziona di nuovo ... vabbè

il link è questo ... sul browser deve andarti
http://api.ipify.org/

Si, come detto, appena avviato mi ha dato l'IP pubblico, ho chiuso il monitor seriale, l'ho riaperto e ... ha dato l'errore.

Si, si, il link lo conoscevo e funziona bene ed ho visto che, anche se si fanno richieste abbastanza frequenti ... non si arrabbia :smiley: :smiley: :smiley:

Guglielmo

Ah ok scusa, mi sono perso un pezzo :slight_smile: ... bene quindi manco il 5500!!

Io mi sono dedicato sempre al webserver, ma praticamente mai a fare di arduino un client, per necessità stavo facendo uno sketch di setup completamente automatico della scheda di rete di arduino, che collegato a qualsiasi rete determinasse in automatico i suoi paramentri con DHCP, ma nello stesso tempo che avesse un IP statico cambiando solo la quarta cifra dell'IP fornito dal protocollo (il numero finale ovviamente conosciuto), inoltre il programma fa anche un test sul cavo di rete scollegato e l'orogio aggiornata tramite NTP, fin qui tutto bene fino a quando mi sono accorto di questo timeout.
Ho provato altri sketch e lo fa lo stesso, alchè mi chiedo, ma quelli che usano pachube o devono inviare dati a un php esterno non lo fanno con un http GET?
Cerco su google ed effettivamente scopro che il problema è rinomato già dal 2010 con la 0020 e 0022 fino a oggi

google "arduino client.connect() socket"
google "client.connect() timeout"
google "client.connect() fail"

I più discussi, qualcuno dice "solved" ... ma non ha solved un bel niente! Le modifiche riportate non risolvono il problema.

http://forum.arduino.cc/index.php?topic=22385.0
http://forum.arduino.cc/index.php?topic=49401.0
https://forum.arduino.cc/index.php?topic=291088.0
http://forum.arduino.cc/index.php?topic=286646.0

Credo che abbiano solo adattato la libreria Ethernet al W5500 chiamandola Ethernet2 senza però cambiare la struttura del codice e quindi ... si porta dietro gli stessi problemi ... ::slight_smile:

Probabilmente è più complesso di quello che sembra ... chissà ...

Guglielmo

Forse ci sono, ridotto il timeout del socket a 2sec invece che 35sec, ridotto i tentativi inutili di connessione da 8 a 2.
Su 100 request al server 96 vanno a buon fine (+/- una al secondo), ma io vorrei il 100%.
Il programma va sistemato e scritto bene. Per dirla in breve, quel "while(client.connected())" va eliminato poichè non va daccordo con "if (client.available())" per una logica di confronti tra vero/falso e di return vari di entrambe le funzioni presenti nella libreria, .... il perchè ci siano è ancora sconosciuto.

Eccolo, un polling verso un server esterno direi accettabile, non dovrebbe commettere errori (quasi mai), ma anche se fallisce non è più bloccante.

#include <SPI.h>
#include <Ethernet.h>
#include "utility/w5100.h"
//#include "utility/socket.h"

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
int serv_port = 80;
EthernetClient client;

void setup(){
 delay(1000);
 pinMode(4, OUTPUT); pinMode(10, OUTPUT);      
 digitalWrite(4, 1); digitalWrite(10, 1);
 Ethernet.begin(mac); //DHCP
 Serial.begin(115200);
 W5100.setRetransmissionTime(0x07D0); //timeout 2000
 W5100.setRetransmissionCount(2); // tentativi
  Serial.print("IP: "); Serial.print(Ethernet.localIP()); //DEBUG
  Serial.print(":"); Serial.println(serv_port); //DEBUG
  Serial.print("SUBNET: "); Serial.println(Ethernet.subnetMask()); //DEBUG
  Serial.print("GATEWAY: "); Serial.println(Ethernet.gatewayIP()); //DEBUG
  Serial.print("DNS SERVER: "); Serial.println(Ethernet.dnsServerIP()); //DEBUG
  Serial.println();
}

void loop(){
   Connetti_server();
   delay(500); // >>> meglio usare un timer con millis() ... 
}

void Connetti_server(){ 
 int16_t c;
 String readstring;  
 unsigned long startTime = millis();
  if (client.connect("api.ipify.org", 80)) {
   //Serial.print(" - Connected");
   client.write("GET / HTTP/1.0\r\nHost: api.ipify.org\r\nConnection: close\r\n\r\n");
   client.println();  
   
 Serial.print("Connessione al server ... ");
 while ((!client.available()) && ((millis() - startTime ) < 2000));

 while (client.available()) { 
        while ((c = client.read())>0){
          //Serial.print((char)c); //debug
           readstring += (char)c;          
           if((char)c=='\n') readstring="";             
       }    
 }
Serial.println(readstring);
 delay(5);

   client.flush();
   client.stop();
   // Serial.println(readString);
    
 } else {
   Serial.println("ERRORE DI CONNESSIONE!");
 }
}

E quanti siti non ti bannano in 5 secondi, appena inizi a sparargli richieste ogni 10 ms?

Ok, funziona anche su W5500 ...
... se, mentre è in funzione, chiudo il monitor seriale e lo riapro ottengo massimo uno o due errori e poi riprende :slight_smile: :

176.46.xxx.xxx
. 176.46.xxx.xxx
Connessione al server ... 176.46.xxx.xxx
Connessione al server ... 176.46.xxx.xxx
Connessione al server ... IP: 192.168.1.156:80
SUBNET: 255.255.255.0
GATEWAY: 192.168.1.1
DNS SERVER: 192.168.1.1

ERRORE DI CONNESSIONE!
ERRORE DI CONNESSIONE!
Connessione al server ... 176.46.xxx.xxx
Connessione al server ... 176.46.xxx.xxx
Connessione al server ... 176.46.xxx.xxx

Guglielmo

Ah ... per farlo funzionare su schede con W5500, oltre alla solita modifica sugli include :

#include <Ethernet2.h>
#include "utility/w5500.h"

... occorre un'altra modifica visto che ... qualche "genio" :smiling_imp: ha istanziato la W5500Class si con il nome w5500 ma con la w minuscola, contrariamente a quanto si ha con Ethernet dove è istanziata come W5100 (W maiuscola), quindi:

w5500.setRetransmissionTime(0x07D0); //timeout 2000
w5500.setRetransmissionCount(2); // tentativi

Guglielmo

Ah ... la libreria Ethernet2 è disponibile all'interno del "Library Manager" quindi, se vuoi fare qualche prova di compilazione per verifiche di compatibilità, puoi tranquillamente installarla da li :wink:

Guglielmo

Non è fatta per sparare pacchetti a server a caso per farli schiantare (per questo ci sono altre cose più divertenti) ... È un test e una forma base... Se tu possiedi uno spazio web e hai bisogno che arduino aggiorni i dati sul tuo sito piuttosto rapidamente si può fare .... Lo sketch mostra che il fallimento della connessione non blocca più il programma ma lo ignora dando spazio al resto delle cosette da fare. Se vedi sul IDE Federico Vanzati aveva trovato un metodo... Le sue modifiche non permettono di ripetere la connessione prima di 36 sec proprio per evitare il timeout... Volevo vedere se era possibile correggere questo timeout e ottimizzare la connessione, ma soprattutto mi intestardisco nel sapere perché ciò accade

Non intendo che può servire a far schiantare i server volutamente, ma sai come funziona qui: un sacco di niubbi prenderanno il tuo codice e lo incolleranno nel loro IDE senza capire alcunché di quel che fa e inizieranno bombardare ipify di richieste. Poi studieranno quel tanto che basta per mettere magari l'indirizzo del loro server su altervista e inizieranno a sobissare di richieste quello. Poi entreranno in gioco i meccanismi di rate limiting, i firewall, le blacklist e quant'altro e li bloccheranno. Quindi verranno qui a lamentarsi che non funziona, ecc ecc. Un po' come col delay() e le String.

Questo meccanismo può avere senso al limite se il server dall'altra parte è tuo, ma a mio avviso è proprio sbagliato usare HTTP per questo scopo (per non parlare dei limiti del polling in sé), si può fare con metodi migliori e soprattutto senza continuare ad aprire e chiudere connessioni ad una velocità folle.

Comunque bravo per l'identificazione e la soluzione del problema :).

Ok, giusto ... metto un while nel setup, solo alla prima connesione riuscita esce e stampa il dato :slight_smile:
Oh comunque sono uscito alle 9.00 e ho lasciato tutto come era è ancora qui che spara request, speriamo che non mi suonino alla porta quelli della ipify ahgahahhahahaha

se, mentre è in funzione, chiudo il monitor seriale e lo riapro ottengo massimo uno o due errori e poi riprende

si gpb, ne sono a conoscenza succede solo all'inizializzazione, perchè il socket è apparantemente occupato dalla precedente connessione con la stessa porta e lo stesso IP host, infatti a ogni connessione l'ip host ospite amazon cambia ogni volta (si viene indirizzati su circa 10 IP diversi).
Non si vede l'IP dell'host ma avevo debuggato la lib EthernetClient.cpp che fa il resolve dell'indirizzo remoto

int EthernetClient::connect(const char* host, uint16_t port) {
  // Look up the host first
  int ret = 0;
  DNSClient dns;
  IPAddress remote_addr;

  dns.begin(Ethernet.dnsServerIP());
  ret = dns.getHostByName(host, remote_addr);
  if (ret == 1) {
  
    //Serial.print("Host: ");  Serial.println(host);       
   // Serial.print("Remote address: "); 
    //Serial.print(remote_addr);  
    //Serial.print(":");  Serial.println(port);  
    
    return connect(remote_addr, port);
  } else {
    return ret;
  }
}

occorre un'altra modifica visto che ... qualche "genio" :smiling_imp: ha istanziato la W5500Class si con il nome w5500 ma con la w minuscola, contrariamente a quanto si ha con Ethernet dove è istanziata come W5100 (W maiuscola),

Provando la ethernet2 ho pensato la stessa identica cosa .... "ma perchè non hanno mantenuto la W maiscola??" :slight_smile: