client.stop() odd behavior in telnet server sketch

Hi All,

I have an Uno R3 and a W5100-based Ethernet/microSD shield. I purchased both in July 2015. I assume the Ethernet shield is a recent revision but I don't know.

I am developing a sketch which will run a "telnet server" on my Uno. I aim to be able to telnet to the Uno and issue a command, probably one character. The Arduino will respond with some data and then disconnect me. Really simple. I am developing on a Mac running OSX Yosemite and I am using the most recent Arduino IDE. My Mac and the Uno/ES are plugged into the same little Netgear switch at my house. Nothing fancy. I am testing with plain old OSX "telnet" running from a plain old Terminal.app.

(Note that this is my first time to use the Ethernet shield, although not my first Arduino sketch and not my first time with "light" client/server network programming (TCL, Perl). For what that's worth :slight_smile: )

My problem is that I cannot get client.stop to disconnect my telnet session. Well, I can, but only if I put the call in a certain place in the sketch. But I cannot if I put it where I want to put it, which should be functionally equivalent, but evidently is not.

My problem is similar to a side problem in this thread, but the side problem is only mentioned at the end and it is ignored because the OP was so happy his main problem was solved, I guess :slight_smile:

I have developed a sketch to illustrate my problem as plainly as I can. In fact, I started with some good, solid code from the thread I referenced. Anyway, here is my example:

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

boolean alreadyConnected = false;
boolean forceDisconnect = false;
int i = 0;
int j = 0;
int timeout = 15000; // give server time to come up.

byte pin_SDCardCS = 4;
EthernetServer server(23);

void setup() {
  Serial.begin( 9600 );
  delay( 1000 );
  Serial.println( "serial started" );
  delay( 1000 );

  pinMode( pin_SDCardCS, OUTPUT );
  digitalWrite( pin_SDCardCS, HIGH );

  byte mac[] = { 0x00, 0x08, 0xDC, 0x00, 0x00, 0x42 }; // WizNet OUI is 00:08:DC .
  IPAddress ip(192,168,177,105);
  IPAddress gw(192,168,177,1);
  IPAddress sm(255,255,255,0);
  Ethernet.begin( mac,ip,gw,sm );
  server.begin();
}

void loop() {
  if( i < timeout ) {
    i++;
  } else {
    forceDisconnect = true;
  }

  EthernetClient client = server.available();

  if( client ) { 
    Serial.println( "\nyes server.available" );

    if( !alreadyConnected ) {  
      Serial.println( "\nnew client" );
      alreadyConnected = true;
    }  else {
      Serial.println( "\nsame old client" );
    }

    while( client.available() ) {
      if( forceDisconnect ) {
        Serial.println( "\nforce disconnecting client inside" );
        client.stop();
        delay( 1000 );
        alreadyConnected = false;
        forceDisconnect = false;
        i = 0;
        timeout = 5000; // can timeout faster now.  but user needs to be quick with the telnet.
      } else {
        Serial.println( "same old client available" );
        char c = client.read();
      }
    }
  } else {
    // we spend a lot of time here, so just output a dot. 100 dots per line.
    // output i at the end of every line so we can keep track of time.
    j++;
    Serial.print( "." );
    if( j > 99 ) {
      j = 0;
      Serial.println( i );
    }
  }

  if( forceDisconnect ) {
    if( alreadyConnected ) {  
      Serial.println( "\nforce disconnecting client outside" );
      client.stop();
      delay( 1000 );
      alreadyConnected = false;
    }
  }

  delay( 1 );
}

I run this code with the IDE serial monitor open for debugging.

If I telnet to the Uno/ES and hit enter a few times I can see the serial debug output indicating it sees the connection and my keystrokes. It also sees some initial telnet garbage ,which I assume is my telnet client attempting to negotiate with the server. No worries there.

However, when the counter reaches 15,000 and the sketch goes to disconnect (outside disconnect) me, it does not. It says it does, but my telnet session remains connected. The sketch only disconnects me if I hit enter after the counter reaches 15,000 (inside disconnect).

It's like the client object is not "hot" or "selected" when the outside disconnect fires. The client object is only "selected" immediately after reading data from it. Or maybe it is only "visible" in the scope where a client.available() is called. Or maybe it is a problem with my code. I am inclined to think this is in fact the case, but I just can't find it.

I have tinkered with this for hours with no luck. Could someone out there please have a look and help me see what I am doing wrong? If any clarification is needed please let me know.

-cls

I have a telnet/persistent tcp server example in the playground. It will handle 4 simultaneous clients. No timeout feature either. I might be able to help you with the timeout if you require it.
http://playground.arduino.cc/Code/Telnet
It has no bells and whistles because i don't know what each user wants to do with it.

Hi SurferTim!

Thanks for pointing me at your sketch. I will load it up and play with it tonight. I assume if I connect with telnet from my terminal, and issue "quit", the disconnect(i); close(i) sequence will actually drop me all the way back to my bash prompt?

If so, that is great. But can you tell me why my posted code doesn't seem to to it consistently? Is client.close() just broken like I think it is?

I really appreciate the server sketch you have provided, but I really want to know why I have to step outside the published, blessed Ethernet library in order to get client disconnection to work.

-cls

Either enter "quit" or just disconnect and it will close the connection. The only time it doesn't close the connection is if the connection breaks, then you would need a timeout to disconnect that client.

Sure. But do you happen to know why you have to resort to lower-level routines in order to reliable close the connection? Why doesn't client.close() work? Please let me know if I need to post this question in another forum.

-cls

The ethernet library is not really aimed at a persistent connection. It is designed to handle one connection at a time. That is why the lower level code. It is the only way I could get the ethernet shield to handle 4 simultaneous connections.

Not trying to be argumentative, but I don't consider the connection I am establishing to be "persistent". Neither persistent as in long-lived nor persistent as in HTTP, where several commands/responses are sometimes sent/received via the same TCP connection. I just want to send one command, get one response, and have the server disconnect me. Also, I am trying to do just one connection at a time (sorry, I guess that wasn't clear). I think mine is a simple use case and that it aligns pretty well with the capabilities of the Ethernet library.

Again - sincerely - not trying to be argumentative, but I am sort of wanting to file a bug and wanted to avoid doing so unnecessarily. Could you please go into more detail, or point me at some material/discussion I can reference?

-cls

I just want to send one command, get one response, and have the server disconnect me.

That is how a web server works. Look for those examples. Telnet requires a persistent connection.

OK, direct question, if I may. Should have asked this in the first place, apologies:Why doesn't client.stop() reliably disconnect a client session connected to an Arduino EthernetServer?

-cls

It does if the client rx buffer is empty. I have a couple server examples in the playground also. They have no problems closing a client connection.

Thanks, SurferTim. I started with your new web server example here and ended up with the following much-improved (over my previous version, that is) telnet server code:

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

unsigned long
  maxIdleMillis    = 1000,
  maxConnectMillis = 5000,
  connectionStartMillis,
  lastActivityMillis;;
byte pin_SDCardCS = 4;
EthernetServer server(23);

void setup() {
  Serial.begin( 9600 );
  delay( 1000 );
  Serial.println( "\nserial started" );
  delay( 1000 );

  pinMode( pin_SDCardCS, OUTPUT );
  digitalWrite( pin_SDCardCS, HIGH );

  byte mac[] = { 0x00, 0x08, 0xDC, 0x00, 0x00, 0x42 }; // WizNet OUI is 00:08:DC .
  IPAddress ip( 192,168,177,105 );
  IPAddress gateway( 192,168,177,1 );
  IPAddress subnet( 255,255,255,0 );
  Ethernet.begin( mac,ip,gateway,subnet );

  server.begin();

  delay( 5000 );
  Serial.println( "telnet now" );
  delay( 5000 );
  Serial.println( "you had your chance" );
}

void loop() {
  EthernetClient client = server.available();

  if( client ) {
    connectionStartMillis = millis();
    lastActivityMillis = connectionStartMillis;
    while( client.connected() ) {
      while( client.available() ) {
        // client is connected and has data available for us to read.
        Serial.write( client.read() );
        lastActivityMillis = millis();
      }
      // client is connected but has no data available for us to read.
      // we spend a lot of time here.
      if( millis() - connectionStartMillis > maxConnectMillis ) {
        Serial.println( "\nmax connection time exceeded" );
        client.stop();
      }
      if( millis() - lastActivityMillis > maxIdleMillis ) {
        Serial.println( "\nidle timeout" );
        client.stop();
      }
      delay(1);
    }
    // client has disconnected.
    Serial.println( "client disconnecting" );
    client.stop();
  } else {
    // no client connected yet.
    delay(1);
  }
}

It is only meant to handle one connection at a time, which it does perfectly. This sketch achieves my goal 100%.

The main improvement is the use of the entire client / client.connected / client.available "suite". I wasn't doing that before.

Again, thanks very much for pointing me in the right direction, SurferTim.

-cls