Das stimmt nicht ganz. Bei TCP gibt es einen TCP-Handshake, bei dem der Client zunächst ein Paket sendet (Flag SYN), auf das der Server mit einem Paket (Flag SYN ACK) antwortet. Das wiederum wird vom Client wieder mit einem "ACK" Paket beantwortet. Damit gilt die TCP-Verbindung als hergestellt, obwohl noch keine echten Daten übertragen wurden.
Der Server kann also durchaus wissen, on ein Client da ist oder nicht.
Die spannende Frage ist, wie das in der Lib und auf dem WizNet Chip implementiert ist. Also wann liefert das server.available() einen Client zurück.
Laut Doku wohl erst, wenn der Client tatsächlich was gesendet hat.
Gets a client that is connected to the server and has data available for reading.
Schaut man sich die beiden verantwortlichen Funktionen in der Klasse EthernetServer und EthernetClient an, sieht man auch warum:
EthernetClient EthernetServer::available()
{
accept();
for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
EthernetClient client(sock);
if (EthernetClass::_server_port[sock] == _port &&
(client.status() == SnSR::ESTABLISHED ||
client.status() == SnSR::CLOSE_WAIT)) {
if (client.available()) {
// XXX: don't always pick the lowest numbered socket.
return client;
}
}
}
return EthernetClient(MAX_SOCK_NUM);
}
int EthernetClient::available() {
if (_sock != MAX_SOCK_NUM)
return W5100.getRXReceivedSize(_sock);
return 0;
}
In EthernetServer::available() sieht man, das zunächst geprüft wird, ob ein Client den Status "Established" (TCP verbindung steht) oder "Close wait" (verbindung soll abgebaut werden, ist aber noch aktiv) hat. Das wäre genau das, was Du benötigst.
Dann wird aber zusätzlich mit dem "if (client.available())" die Funktion EthernetClient::available() aufgerufen. Und die liefert nur was sinnvolles zurück, wenn schon Daten übermittelt wurden "return W5100.getRXReceivedSize(_sock);". Wenn die empfangene Datengröße 0 ist, entspricht das im If-Statement einem "false".
Ich habe mir mal den Spass gemacht und den Code in
wiefolgt angepasst:
EthernetClient EthernetServer::available()
{
accept();
for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
EthernetClient client(sock);
if (EthernetClass::_server_port[sock] == _port &&
(client.status() == SnSR::ESTABLISHED ||
client.status() == SnSR::CLOSE_WAIT)) {
/**
if (client.available()) {
// XXX: don't always pick the lowest numbered socket.
return client;
}
**/
return client;
}
}
return EthernetClient(MAX_SOCK_NUM);
}
Damit passiert dann folgendes:
nc -v 192.168.0.211 25
Connection to 192.168.0.211 25 port [tcp/smtp] succeeded!
Hallo da draussen
Daran, das die Ausgabe der Servers direkt unter der "succeeded" Zeile steht erkennt man, das ich nicht Enter drücken musste.
Was zumindest erstmal meine Theorie beweist. Allerdings wäre ich nun vorsichtig, meine Änderung einfach zu übernehmen. Denn es kann durchaus sein, das sich andere Teile der Lib darauf verlassen, das EthernetServer::available() nur dann einen Client zurückliefert, wenn schon Daten gesendet wurden.
Hier noch der Code mit dem ich das auf dem Arduino getestet habe:
/*
Chat Server
A simple server that distributes any incoming messages to all
connected clients. To use telnet to your device's IP address and type.
You can see the client's input in the serial monitor as well.
Using an Arduino Wiznet Ethernet shield.
Circuit:
* Ethernet shield attached to pins 10, 11, 12, 13
* Analog inputs attached to pins A0 through A5 (optional)
created 18 Dec 2009
by David A. Mellis
modified 9 Apr 2012
by Tom Igoe
*/
#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, 0xB2, 0x3F, 0xaE, 0xcD };
IPAddress ip(192,168,0, 211);
IPAddress gateway(192,168,0, 252);
IPAddress subnet(255, 255, 255, 0);
// telnet defaults to port 23
EthernetServer server(25);
boolean alreadyConnected = false; // whether or not the client was connected previously
void setup() {
// 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);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
Serial.print("Chat server address:");
Serial.println(Ethernet.localIP());
}
void loop() {
// wait for a new client:
EthernetClient client = server.available();
// when the client sends the first byte, say hello:
if (client) {
Serial.println("client connected");
client.println("Hallo da draussen");
client.flush();
while(client.connected()) {
if (client.available() > 0) {
// read the bytes incoming from the client:
char thisChar = client.read();
// echo the bytes back to the client:
server.write(thisChar);
// echo the bytes to the server as well:
Serial.write(thisChar);
}
}
Serial.println("client disconnected");
}
}
Bei Bedarf kann ich auch noch den tcpdump Mitschnitt zur Analyse liefern 