Go Down

Topic: What does client.stop() do and how does if(client) work from the server side? (Read 310 times) previous topic - next topic

rms5643

Hello!

I'm new to Arudino and one of my first projects I'm trying to accomplish is to get a server to show if it has a connection to the client by turning on an LED and turning it off if the client disconnects or the server disconnects.

Though, there are two things I'm not quite sure I understand from the server side of things:


  • When I call client.stop() on the client side, it disconnects from the server, but when I call client.stop() from the server side, the client still thinks it's connected. Is this intended behavior?

  • If(client) from the client side always evaluates as true as long as it is connected to the server (Regardless if I call client.stop from the server side), but from the server side if(client) is only true as long as a message has been sent from the client to the server. As soon as the message is read and the client hasn't sent anything, if(client) will resolve false.



Is there any resources I can take a look at to get a more in-depth explanation of how these work as opposed to the reference online which only gives a one sentence description?

Thank you!

SurferTim

Quote
When I call client.stop() on the client side, it disconnects from the server, but when I call client.stop() from the server side, the client still thinks it's connected. Is this intended behavior?

If the client doesn't detect the server has sent a disconnect message, you have a problem with your code. When the server code is finished sending packets, it should call client.stop() to send the disconnect message. You must empty the client socket rx buffer before the connection will close on the client end. This is a basic example of the code I use on the client end. It waits for the server to send the disconnect message, then closes its end.
Code: [Select]
while(client.connected()) {
   while(client.available()) {
      Serial.write(client.read());
   }
}
client.stop();

I use a timeout in that code also to prevent that "while(client.connected())" loop from becoming endless if the server stalls or the connection fails, but i left it out for simplicity.

lemming

Most Arduino Webserver examples have the following code:

Code: [Select]

void loop() {
 // listen for incoming clients
 EthernetClient client = server.available();
 if (client) {
   Serial.println("new client");
   // an http request ends with a blank line
   boolean currentLineIsBlank = true;
   while (client.connected()) {
     if (client.available()) {
       char c = client.read();


while Arduinos defacto ethernet expert, SurferTims,  code usually has the following lines:

Code: [Select]

  while (client.connected()) {
     while(client.available()) {
       char c = client.read();


What is the rationale behind using the "while" within while loop instead of the "if" within the while loop?

SurferTim

Quote
What is the rationale behind using the "while" within while loop instead of the "if" within the while loop?

Speed. The w5100 receives the data in packets. By using the "while(client.available())" loop instead of "if", the "while(client.connected())" does not need to be evaluated every iteration. The "client.connected()" will not evaluate to false (exit the loop) until the client rx buffer is empty.

rms5643

Thanks for all of your clarifications. This is making much more sense. Another question: On the server side, I've noticed that if(client) is only evaluated to true if there are bytes available on the client rx socket. Thus, to initiate a connection with the server, the client has to send a least 1 char to the server. Is there a way for the server to recognize if a client is connected before a line like  client.println("Hi"); is evaluated on the client side? Does the server even know a client is connected when client.connect(server, myPort); is evaluated on the client side?

SurferTim

That is a limitation of the ethernet library, not the w5100. The server must receive at least one character to set "server.available()" to true.

You can use the w5100 library code to determine if a client is connected to a server socket and hasn't sent anything yet, but is more complex than using the ethernet server library. This function should get you started in the right direction to read the status of each socket. It will show clients connected before they send anything.
Code: [Select]
#include <utility/w5100.h>

byte socketStat[MAX_SOCK_NUM];

void ShowSockStatus()
{
  for (int i = 0; i < MAX_SOCK_NUM; i++) {
    Serial.print(F("Socket#"));
    Serial.print(i);
    uint8_t s = W5100.readSnSR(i);
    socketStat[i] = s;
    Serial.print(F(":0x"));
    Serial.print(s,16);
    Serial.print(F(" "));
    Serial.print(W5100.readSnPORT(i));
    Serial.print(F(" D:"));
    uint8_t dip[4];
    W5100.readSnDIPR(i, dip);
    for (int j=0; j<4; j++) {
      Serial.print(dip[j],10);
      if (j<3) Serial.print(".");
    }
    Serial.print(F("("));
    Serial.print(W5100.readSnDPORT(i));
    Serial.println(F(")"));
  }
}

A socket status list:
0x0 = available.
0x14 = socket waiting for a connection
0x17 = socket connected to a client if a server, or a server if a client.
0x22 = UDP socket.

rms5643

Thank you for all of your help. This is more enough for me to get started! Though, I'm still having some problems with my code. My objectives are as follows:

  • When a button is pressed, client connects to the server and sends "Q"

  • Server responds with "A"

  • Server sends disconnect message

  • Client disconnects



My problem is that when the server calls client.stop(), my client thinks it's still connected, that is, while (client.connected()) still evaluates as true. I'm looking at the serial monitors and I see "Q" sent to the server, "A" received from the client, and the disconnect message is sent from the server, but the disconnect message is never received from the client.

So my question: If client.stop() is called from the server side, shouldn't client.connected() be evaluated as false on the client side?

Here's my code for the client:
Code: [Select]

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

// Enter a MAC address and IP address for your controller below.
// The ip address will be dependent on your local network:
byte mac[] = {
 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,1,2);
const int led = 7;
const int button = 3;
const String serverIP = "192.168.1.1";
int buttonState = 0;
boolean isConnected = false;

// Enter the IP address of the server you're connecting to:
IPAddress server(192,168,1,1);

// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (Port 23 is default)
EthernetClient client;

void setup() {
 // setup the pin mode for the LED & button
 pinMode(led, OUTPUT);
 pinMode(button, INPUT);

 // start the Ethernet connection:
 Ethernet.begin(mac, ip);
 //Open serial communications and wait for the port to open:
 Serial.begin(9600);

 // give the Ethernet shield a second to initialize:
 delay(1000);
 Serial.print("Will connect to ");
 Serial.println(serverIP);
}

void loop() {
 // read the state of the pushbutton value:
 buttonState = digitalRead(button);

 // if the button is pressed, connect to the server:
 if (buttonState == HIGH && !isConnected) {
   // if you get a connection, report back via serial,
   // turn on the LED, and set the button flag:
   if (client.connect(server,23)) {
     isConnected = true;
     digitalWrite(led, HIGH);
     Serial.println("Server found");
     client.print('Q');
   }
   else {
     // if you didn't get a connection to the server:
     Serial.println("Connection failed");
   }
 }

 // if there are incoming bytes available
 // from the sever, read them and print them
 // to the serial queue and send them out to
 // the socket if it's open:
 while (client.connected()) {
   while (client.available()) {
     Serial.write(client.read());
     Serial.println("while.available()");
   }
         Serial.println("while.connected()");
         delay(1000);
 }

 // if the server's disconnected, stop the client
 if (isConnected == true ) {
   isConnected == false;
   client.stop();
   digitalWrite(led, LOW);
   Serial.println("Stopping client");
 }

 delay(1000);
}


And here's the code for my server:
Code: [Select]

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

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network.
// gateway and subnet are optional:
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF };
IPAddress ip(192,168,1, 1);
IPAddress gateway(192,168,1, 1);
IPAddress subnet(255, 255, 0, 0);
const int led = 7;

// telnet defaults to port 23
EthernetServer server(23);
boolean alreadyConnected = false; // whether or not the client was connected previously

void setup() {
  // setup the pin mode for the LED & button
  pinMode(led, OUTPUT);

  // initialize the ethernet device
  Ethernet.begin(mac, ip, gateway, subnet);
  // start listening for clients
  server.begin();
  // Open serial communications and wait for port to open:
  Serial.begin(9600);

  Serial.print("Chat server address:");
  Serial.println(Ethernet.localIP());
}

void loop() {
 // wait for a new client:
 EthernetClient client = server.available();

 // when the client send sends a question, provide an answer:
 if (client) {
   Serial.println("Client found");
   if (!alreadyConnected) {  
     Serial.println("1st time client found");
     // turn on status LED
     digitalWrite(led, HIGH);
     alreadyConnected = true;
   }

 // if there are incoming bytes available
 // from a client, read them and print them
 // to the serial queue and send them out to
 // the socket if it's open:
     while (client.available()) {
       Serial.write(client.read());
       Serial.println("while.available()");
     }
     delay(1000);

   client.print('A');
 }
 // when client/server communication is complete, disconnect:
 else if (alreadyConnected) {
   //alreadyConnected = false;
   digitalWrite(led, LOW);
   Serial.println("Stopping client");
   client.stop();
 }

 delay(1000);
}

SurferTim

It appears to be your server code not sending the stop(). Try adding this after sending the 'A'.
Code: [Select]
    client.print('A');
    client.stop();


rms5643


It appears to be your server code not sending the stop(). Try adding this after sending the 'A'.
Code: [Select]
    client.print('A');
    client.stop();




That worked! Eureka! Though, I'm not sure why... In the serial monitor, I can see on the servers serial monitor "Stopping client.." which is right before client.stop() is called, yet that message never seems to sent? Any explanations?

Thanks for all of your help!

SurferTim


rms5643


I think it is the else. It may not execute at the right time.


I think you're right. I removed the else and now it's working as it should. Previously, after the the server exhausted the bytes on the rx socket, naturally, the else if would not evaluated until the next iteration of the loop. I guess that extra time caused it to fail sending/client receiving the disconnect message.

Go Up