JAndrassy / WiFiEspAT

Arduino networking library. Standard Arduino WiFi networking API over ESP8266 or ESP32 AT commands.
GNU Lesser General Public License v2.1
288 stars 44 forks source link

[question] getting client link id #5

Closed atesin closed 4 years ago

atesin commented 4 years ago

hi ... i would like to get the client link id, to write dynamic content directly to esp someway like this...

how can i get client link id? (the example below is the simpler solution i thought, is a little difficult to me since i am not so skilled in programming)

// setup
SoftwareSerial Serial1(6, 7); // RX, TX
Serial1.begin(9600);
WiFi.init(Serial1);
//...
WiFiServer server(80);
server.begin();

// loop
WiFiClient client = server.available();
if (!client)
  return;

byte linkId = client.getLinkId(); // SOMETHING LIKE THIS

// example: print dynamic web page with client ip address
char header1[] = "HTTP/1.1 200 OK\r\n";
char header2[] ="Server: arduino/esp\r\n\r\n";
char dynamicPage[] = "your ip address is: ";

char clientIp[20];
strcpy(clientIp, client.remoteIp()); // don't know if this will work, but you got the idea

// AT+CIPSEND=(linkId),(length)\r\n
// >(data)

Serial1.write("AT+CIPSEND=")
Serial1.write(linkId); // PASSING LINK ID HERE
Serial1.write(",");
Serial1.writeln(
  strlen(header1) + // sum lengths
  strlen(header2) +
  strlen(dynamicPage) +
  strlen(clientIp)
);

client.find(">");
Serial1.write(header1); // writing composite response
Serial1.write(header2);
Serial1.write(dynamicPage);
Serial1.write(clientIp);
JAndrassy commented 4 years ago

you can do the same with a WiFiClient object. start with the WebClient example

JAndrassy commented 4 years ago

sorry, now I understand. your goal is to send all data in one TCP packet. Are you sure every CIPSEND is send immediately as a TCP packet and the esp8266 doesn't wait a little for more data?

atesin commented 4 years ago

yes... i want to send all dynamic data at once in one AT+CIPSEND command to minimize overhead and delays... instead of send

AT+CIPSEND=0,24
>HTTP/1.1 200 OK\r\n
SEND OK

AT+CIPSEND=0,31
>Server: arduino/esp\r\n\r\n
SEND OK

AT+CIPSEND=0,27
>your ip address is: 
SEND OK

AT+CIPSEND=0,11
>192.168.1.4
SEND OK

about the question, i find very difficult another thread are sending another command at the same time when i send the cipsend command throught uart tx line, because i think arduino is single threaded, so i find unlikely collissions or blockings may occur,.. so basicaly what i do is send all related commands in block sequence

var string1
var string2
...
AT+CIPSEND=(link),(sum_of_string_lengths) // ok
find(">") // esp listening, send strings now
string1
string2
...
SEND OK // receive status

but for all this to work i need to know the link id ... i know maybe is hidden due some kind of encapsulation, but that is this question is for

or maybe can implement some esp "prepared print" function/object like sql prepared statement or OB php functions or some varargs print, idk

JAndrassy commented 4 years ago

the library has buffering. data are not send in so small pieces. turn on debug and you will see. you can set the buffer size at compile time or use a larger buffer in your code for example with my StreamLib BufferedPrint class https://github.com/jandrassy/WiFiEspAT#advanced-use

atesin commented 4 years ago

thanks for your interest

yes i know it has buffers, 32 bytes you said

maybe i deviate the conversation, i want return to "constructing a response summing all string lengths and passing them all directly to esp"

i am trying to develop a webserver, with a home page (just an html form to enter wifi auth) and some "scripts" that take the submitted form to processing (try access internet with wifi credentials to contact server in background)

my http headers are about 100 bytes stored in progmem (depending on status code, browser caching options, encodings, redirections, etc), the home page itself is about 300 bytes in progmem (including head tags, prevent favicon/robot/etc request, style, javascripts, frames, etc), there are a couple more pages stored in progmem as "F()" strings too, the "dynamic" data is stored in char arrays ... and the server must assemble the full response depending the situation

i think we can pass responses directly to esp from progmem or char arrays (several bytes), without any intermediate buffers for simplicity and optimization (the Stream.print() accept many parameter types)... all we have to know to pass data are the strings itselves (stored wherever), its total lengths, and the LINK ID

maybe adding some byte id() {return linkId;} function =D

... and in the future something like new BufferedPrint(client) object with addString(pstr) and printAll() functions

... or maybe something like Client::printAll(varargs...) or Client::printf(types, params...) =D

JAndrassy commented 4 years ago

this library will not support 'open' command. it always handles the complete AT command and response before returning control to user code. and it is for beginners to use with the well know WiFi library API, which doesn't support the proposed approach

you still didn't check how many TCP packets are send (with WireShark for example). you could use faster UART baud rate to deliver the data to AT firmware faster so it can join them to less packets.

and you can do your web server with WiFiClient efficient. if you send a 500 bytes buffer, it will be CIPSEND as 500 bytes. and you can send a complete progmem string from progmem.

StreamLib's classes have printf

it is better to serve large html as file and request the data then with AJAX. see for example the WebServer in my Ragulator project

atesin commented 4 years ago

i agree with you about encapsulation of AT commands, i understood it is easier for beginners (like me) and more error proof, as i mentioned earlier

but i am not really talking about sending a large string directly from progmem to esp, but a composite one (many little strings, from wherever) in one single passthrough at command, to optimize process minimizing serial traffic and ram usage due buffers

in the meanwhile i will try to add the following function to library and tell you how it resulted (i know is not a very elegant solution but will do with the hope it serves me)

// WiFiClient.h
public:
  uint8_t id();

// WiFiClient.cpp
uint8_t WiFiClient::id() {
  return linkId;
}
atesin commented 4 years ago

my basic test was successful... the response took about 20ms ... it still have to handle request, caching options and block auto favicon req

(... but it overflown and dropped about the half of request headers :( ... did it use the new ATv1.7 2kb esp passive mode serial cache? )

wifiespat-passive

esp serial console

Serial enabled, searching for ESP-01...
Waiting for communication with WiFi module
Waiting for connection to WiFi.
Connected to WiFi network, To access the server, enter "http://192.168.218.161/" in your browser.
new incoming client: 192.168.218.151
GET / HTTP/1.1
Host: 192.168.218.161
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36 OPR/65.0.3467.48
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: es-ES,es;q=0.9

client served and disconnected: 192.168.218.151

port 80 telnet

[root@testSrv ~]# echo foobarbaz | nc 192.168.218.161 80
HTTP/1.1 200 OK
Server: WiFiEspAT

your ip address is: 192.168.218.218

the sketch

// uses esp8266 LoBo firmware
// https://github.com/loboris/ESP8266_AT_LoBo

// uses patched WiFiEspAT library
// https://github.com/jandrassy/WiFiEspAT/issues/5#issuecomment-560554236

//--------- macros ------------

// to simulate Serial.println() concatenation, example: echo(F("ratio = ") _ val _ '%');
#define echo(args) do{Serial.print(args);Serial.println();}while(0)
#define _ );Serial.print(

// similar to F() but for "_P" pgmspace functions, example: strlen_P( P("foo") )
#define P(args) (PGM_P) F(args)

// for functions could accept or return F() strings, examples:
// - func(F("foo"))  -->  void func(S fstr){Serial.println(fstr);}
// - S sta = status()  -->  S status(){return F("OK"));}
// - S fstr = F("upper string");
#define S const __FlashStringHelper *

//--------- global --------

#include "SoftwareSerial.h"
#include <WiFiEspAT.h>

SoftwareSerial esp(7, 8); // RX, TX
WiFiServer server(80);

//--------- setup --------

void setup()
{
  Serial.begin(9600); // enable serial console
  /*DEBUG*/ while (!Serial);
  delay(1000); // don't check serial connection to prevent stucks
  echo(F("Serial enabled, searching for ESP-01...")); // echo

  esp.begin(19200); // enable software serial, connected to esp-01
  WiFi.init(esp);

  Serial.print(F("Waiting for communication with WiFi module"));
  while (WiFi.status() == WL_NO_MODULE)
  {
    delay(2000);
    Serial.print('.');
  }

  // waiting for connection to Wifi network set with the SetupWiFiConnection sketch
  Serial.print(F("\r\nWaiting for connection to WiFi"));
  while (WiFi.status() != WL_CONNECTED) {
    delay(2000);
    Serial.print('.');
  }

  server.begin();

  IPAddress ip = WiFi.localIP();
  echo(F("\r\nConnected to WiFi network, To access the server, enter \"http://") _ ip _ F("/\" in your browser."));
}

//---------- loop -----------

void loop()
{
  WiFiClient client = server.available();
  if (!client)
    return;

  while (!client.status()); // i found calling remoteIP() too early gives 0.0.0.0
  IPAddress ip = client.remoteIP();
  echo(F("new incoming client: ") _ ip);

  while (client.available())
    Serial.print((char) client.read()); // flush the incoming data to the console, whatever it is (just for testing)

  // process dymanic content

  S headers = F("HTTP/1.1 200 OK\r\nServer: WiFiEspAT\r\n\r\n");
  S htdoc = F("your ip address is: ");

  char ipAddr[16]; // ip address could be variable length
  sprintf_P(ipAddr, P("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);

  // send response

  byte link = client.id(); // this is the patch i am testing!

  esp.print(F("AT+CIPSEND="));
  esp.print(link);
  esp.print(',');
  esp.println(strlen_P((PGM_P) headers) + strlen_P((PGM_P) htdoc) + strlen(ipAddr));
  esp.find('>');

  esp.print(headers);
  esp.print(htdoc);
  esp.print(ipAddr);
  esp.find("OK\r\n");

  client.stop();
  echo(F("client served and disconnected: ") _ ipAddr);
}
JAndrassy commented 4 years ago

(... but it overflown and dropped about the half of request headers :( ... did it use the new ATv1.7 2kb esp passive mode serial cache? )

passive mode is activated in WiFi.init(). you can turn on debug to see all AT commands

what overflown, this?

while (client.available())
    Serial.print((char) client.read());

EDIT: with SoftwareSerial use 9600 baud

atesin commented 4 years ago

i am very happy the test resulted ok =D ... there are however some other details outside this test

according you said, passive mode should be enabled with WiFi.init(esp); as seen in setup() function (undocumented)

  esp.begin(19200); // enable software serial, connected to esp-01
  WiFi.init(esp);

until i suspected data loss, debug shows i was wrong (but i think is ok to have doubts and check them)

below are the headers sent by opera browser version 65.0.3467.48 (you can also find the user-agent header) ... i could clearly see the lib reads data in 32 bytes chunks =D

i did some researches (but not tests) and concluded the fastest reliable speed for SoftwareSerial is 19200, while Serial by convention could be as fast as 115200 ... so for this test i switched 19200 for swserial and 115200 for hwserial witn no problem... the response now took 926ms

GET / HTTP/1.1
Host: 192.168.218.161
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36 OPR/65.0.3467.48
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: es-ES,es;q=0.9

meanwhile in the serial console

Serial enabled, searching for ESP-01...
esp INFO: soft reset
esp> AT+RST ...sent
esp> OK ...ignored
esp> 
#qX⸮z⸮⸮ ...ignored
esp> 1⸮⸮a⸮⸮P!⸮N⸮   ⸮!⸮K{{{{⸮⸮⸮#bRJ{{{⸮r⸮)⸮!kG⸮⸮Ja0 !H⸮H ...ignored
esp> ##⸮c⸮⸮ ...ignored
esp> ready ...matched
esp> ATE0 ...sent
esp> ATE0 ...ignored
esp> OK ...matched
esp> AT+CIPMUX=1 ...sent
esp> OK ...matched
esp> AT+CIPRECVMODE=1 ...sent
esp> OK ...matched
esp> AT+CWMODE? ...sent
esp> +CWMODE:1 ...matched
esp> OK ...matched
Waiting for communication with WiFi moduleesp INFO: wifi status
esp> AT+CIPSTATUS ...sent
esp> STATUS:5 ...matched
esp> OK ...matched

Waiting for connection to WiFiesp INFO: wifi status
esp> AT+CIPSTATUS ...sent
esp> STATUS:5 ...matched
esp> OK ...matched
.esp> WIFI CONNECTED ...ignored
esp> WIFI GOT IP ...ignored
esp INFO: wifi status
esp> AT+CIPSTATUS ...sent
esp> STATUS:2 ...matched
esp> OK ...matched
esp INFO: begin server at port 80
esp> AT+CIPSERVERMAXCONN=1 ...sent
esp> OK ...matched
esp> AT+CIPSERVER=1,80 ...sent
esp> OK ...matched
esp> AT+CIPSTO=60 ...sent
esp> OK ...matched
esp INFO: STA IP query
esp> AT+CIPSTA? ...sent
esp> +CIPSTA:ip:"192.168.218.161" ...matched
esp> +CIPSTA:gateway:"192.168.218.1" ...matched
esp> +CIPSTA:netmask:"255.255.255.0" ...matched
esp> OK ...matched

Connected to WiFi network, To access the server, enter "http://192.168.218.161/" in your browser.
esp> 0,CONNECT ...processed
esp> +IPD,0,458 ...processed
esp INFO: incoming linkId 0
esp INFO: status of link 0
esp> AT+CIPSTATUS ...sent
esp> STATUS:3 ...matched
esp> +CIPSTATUS:0,"TCP","192.168.218.151",52709,80,1 ...matched
esp> OK ...matched
new incoming client: 192.168.218.151
esp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,32 ...sent
esp> +CIPRECVDATA,32 ...matched
esp> OK ...matched
esp INFO:   got 32 bytes on link 0
GET / HTTP/1.1
Host: 192.168.21esp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,32 ...sent
esp> +CIPRECVDATA,32 ...matched
esp> OK ...matched
esp INFO:   got 32 bytes on link 0
8.161
Connection: keep-alive
Cesp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,32 ...sent
esp> +CIPRECVDATA,32 ...matched
esp> OK ...matched
esp INFO:   got 32 bytes on link 0
ache-Control: max-age=0
Upgradeesp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,32 ...sent
esp> +CIPRECVDATA,32 ...matched
esp> OK ...matched
esp INFO:   got 32 bytes on link 0
-Insecure-Requests: 1
User-Agenesp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,32 ...sent
esp> +CIPRECVDATA,32 ...matched
esp> OK ...matched
esp INFO:   got 32 bytes on link 0
t: Mozilla/5.0 (Windows NT 6.1; esp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,32 ...sent
esp> +CIPRECVDATA,32 ...matched
esp> OK ...matched
esp INFO:   got 32 bytes on link 0
Win64; x64) AppleWebKit/537.36 (esp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,32 ...sent
esp> +CIPRECVDATA,32 ...matched
esp> OK ...matched
esp INFO:   got 32 bytes on link 0
KHTML, like Gecko) Chrome/78.0.3esp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,32 ...sent
esp> +CIPRECVDATA,32 ...matched
esp> OK ...matched
esp INFO:   got 32 bytes on link 0
904.97 Safari/537.36 OPR/65.0.34esp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,32 ...sent
esp> +CIPRECVDATA,32 ...matched
esp> OK ...matched
esp INFO:   got 32 bytes on link 0
67.48
Accept: text/html,applicaesp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,32 ...sent
esp> +CIPRECVDATA,32 ...matched
esp> OK ...matched
esp INFO:   got 32 bytes on link 0
tion/xhtml+xml,application/xml;qesp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,32 ...sent
esp> +CIPRECVDATA,32 ...matched
esp> OK ...matched
esp INFO:   got 32 bytes on link 0
=0.9,image/webp,image/apng,*/*;qesp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,32 ...sent
esp> +CIPRECVDATA,32 ...matched
esp> OK ...matched
esp INFO:   got 32 bytes on link 0
=0.8,application/signed-exchangeesp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,32 ...sent
esp> +CIPRECVDATA,32 ...matched
esp> OK ...matched
esp INFO:   got 32 bytes on link 0
;v=b3
Accept-Encoding: gzip, deesp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,32 ...sent
esp> +CIPRECVDATA,32 ...matched
esp> OK ...matched
esp INFO:   got 32 bytes on link 0
flate
Accept-Language: es-ES,esesp INFO: get data on link 0
esp> AT+CIPRECVDATA=0,10 ...sent
esp> +CIPRECVDATA,10 ...matched
esp> OK ...matched
esp INFO:   got 10 bytes on link 0
;q=0.9

esp INFO: sync
esp> AT+CIPSTATUS ...sent
esp> STATUS:3 ...matched
esp> +CIPSTATUS:0,"TCP","192.168.218.151",52709,80,1 ...matched
esp> OK ...end of list
esp> AT+CIPRECVLEN? ...sent
esp> +CIPRECVLEN:0,0,0,0,0 ...matched
esp> OK ...matched
esp INFO: close link 0
esp> AT+CIPCLOSE=0 ...sent
esp> 0,CLOSED ...processed
esp INFO: closed linkId 0
esp> OK ...matched
client served and disconnected: 192.168.218.151

thanks for all

JAndrassy commented 4 years ago

you read the request byte by byte so the internal buffer is used. for ATmega with less then 2303 bytes of SRAM the buffer is 32 bytes large. for all other MCUs it is 64 bytes. the size is customizable as described in README.md


I run your test skech and the response was displayed in browser


i found calling remoteIP() too early gives 0.0.0.0

this is still valid? I don't have this problem (with Espressif AT 1.7.1)

atesin commented 4 years ago

i found calling remoteIP() too early gives 0.0.0.0

client.remoteIP(); always gave me 0.0.0.0, i had to look at the library code fo figure out what could be going on ... anyway, it run me flawless with while(!client.status());

note: the use of WiFi.init() to enable passive mode is undocumented

JAndrassy commented 4 years ago

note: the use of WiFi.init() to enable passive mode is undocumented

what is the problem with that? you can't avoid init(). and the doc everywhere has that the lib uses passive mode. where else would it be set?

atesin commented 4 years ago

relax ... you're right

i'm still very happy the test results .. now i am playing with webservers and webservices =D

thanks for all

void loop()
{
  WiFiClient peer = webserver.available();
  if (peer)
    espParseRead(peer);
}

// util

void espSendLength(WiFiClient peer, int len)
{
  esp.print(F("AT+CIPSEND="));
  esp.print(peer.id());
  esp.print(',');
  esp.println(len);
  esp.find("\r\n>");
}

int Fstrlen(S fString)
{
  return strlen_P((PGM_P) fString);
}

// functions

void espParseRead(WiFiClient peer)
{
  //*DEBUG*/ echo(F("|httpParseRead()"));

  while (!peer.status()); // wait until connection ready

  char input[32];
  input[peer.readBytesUntil(' ', input, 31)] = '\0';

  /*DEBUG*/ echo(F("|httpParseRead() input = ") _ input);

  // EACH DAEMON IS RESPONSIBILE FOR FLUSH AND CLOSE ESP BUFFER

  // GET: parse browser request as server
  if ( httpd && !strcmp_P(input, P("GET")) )
    httpParseRequest(peer, input); // recycle input

  // HTTP/*: parse webserver response as client
  else if ( !strncmp_P(input, P("HTTP/"), 5) )
    ;// httpParseResponse(link, dataLen, input); // recycle input

  // flush and close
  else
  {
    while (peer.available())
      peer.read();
    peer.stop();
    echo(F("transaction finished"));
  }
}

void httpParseRequest(WiFiClient browser, char *input)
{
  //*DEBUG*/ echo(F("|httpParseRequest()"));

  // read url
  input[browser.readBytesUntil(' ', input, 31)] = '\0';

  /*DEBUG*/ echo(F("|httpParseRequest() input = ") _ input);

  if ( !strcmp_P(input, P("/mac")) )
    htmlMac(browser, input);

  else
    httpSend404(browser);

  // flush and close
  while (browser.available())
    browser.read();
  browser.stop();
  echo(F("transaction finished"));
}

void htmlMac(WiFiClient browser, char *input)
{
  //*DEBUG*/ echo(F("|htmlMac()"));

  S html1 = F(
    "<html><head><link rel='icon' href='http://0.0.0.0/'/></head><body>\n"
    "<iframe name='hf' style='display:none;'></iframe>\n"
    "<p>dispositivo encontrado, conectese nuevamente a su wifi para continuar</p>\n"
    "<button onclick='go();'>Go!</button>\n"
    "<script type='text/javascript'>\n"
    "  //setInterval(function(){\n"
    "  function go(){\n"
    "    open('http://my.public.webserver/webservice/register-mac.php?"
  );

  espGetMac(input);

  /*DEBUG*/ echo(F("|htmlMac() input = ") _ input);

  S html2 = F(
    "','hf');\n"
    "//  },2000);\n"
    "  }\n"
    "</script>\n"
    "</body></html>\n"
  );

  espSendLength(browser, Fstrlen(httpHeader200()) + Fstrlen(html1) + strlen(input) + Fstrlen(html2));
  esp.print(httpHeader200());
  esp.print(html1);
  esp.print(input);
  esp.print(html2);
  esp.find(OK);
}
JAndrassy commented 4 years ago

I added getLinkId. It returns WIFIESPAT_NO_LINK if the client is unconnected. The valid range is from 0 to WIFIESPAT_LINKS_COUNT.

atesin commented 4 years ago

thanks

i know the trick i did to send compound data in one single command is a little dirty,. and until it works maybe is not the best ...

i think the right, clean, native, and a more elegant solution without "low-level" commands outside the library, instead a bunch of "cipsend(link, sum_length); print, print, print, print" etc. would be simply a "printAll(varargs...)" or some like "printf("format", params...)" ... but as my programming knowledge is limited, i had to be creative to achieve my goal :)

but in the other hand, as i think you are a pro developer., i knew you would do it a lot better than me... thanks again =D

JAndrassy commented 4 years ago

I added two new functions client.write(file) and client.write(callback). the second enables to write data with one AT+CIPSENDEX. see the README and the new SDWebServer example

atesin commented 4 years ago

you are the best

atesin commented 4 years ago

hi... i miss the function WiFiClient.getLinkId() , it was very useful to build a compound response, then send total sizes, then send it all together though the link

in addition, currently i am trying to store WiFiClients in a global array as shown in AdvancedChatServer.ino example... when a client connects, i get it with WiFiServer.accept() to send a welcome/motd/status message, and iterate the global array to store client for later uses.... if i could get linkId i could use it as array index to directy store/retrieve client without the need for iterations !!! (and log events, and print server connections status, etc) =D

currently i know it can be done anyway with no need to modify the library, but in a very unoptimized way:

... i would like linkId back to avoid doing this

maybe you dropped getLinkId() to discourage its use in favor of bufferedPrint for the sake of robustness, but besides "compound prints" linkId could be (actually potentially IS) very useful and versatile for many other possible applications...

i think each user is responsibile to make use of the library in their most convenient way, you haven't to worry about that... i want to motivate you to give us more tools and flexibility to do better applications

thanks for your time and efforts

JAndrassy commented 4 years ago

I had to remove access to linkId because it was not unique. if a WiFiClient has still data stored to read but is already closed the linkId is used by other connection.

you have the write(callback) to send with one CIPSEND.

atesin commented 3 years ago

surely are not unique, they hasn't to be either

... i understand linkId's are not a client property, but actually managed by the SERVER ... they are like reutilizable "pipes", "subprocesses" or "listening workers" that server opens to "link" tcp socket everytime the client sends a packet.... anyway, with this in mind it still can be useful for a lot of things

imagine a modular webserver running in esp8266 with your library.... a single webpage that requests a basic .css "file", some .js and maybe a .json, another basic page in a frame and maybe a little image

if "Connection: close" header is defined (maybe because is more complicated to implement persistent connections), when client retrieves the full webpage, every request will open a new pipelined "link", maybe even recycling them at some point, actually there is no way to manage the links.... so every link will be different and random althought all they are to render the same web page and comes from the same client, so can't rely on them to "identify" clients (what is an esp8266 "client" anyway... )

furthermore, imagine a telnet or simple messaging protocol..... the client can set transfer mode as character, block, line, delimiters or whatever... every "command" could be chunked and each one will open a new link, althought all they belong to the same sequence, command, client or the same session

anyway.... tcp is connectionless so there is no point in try to track links .... but if you really need it, the application itself can manage some kind of "session id" or "cookie" with the traffic, and you can track SESSIONS instead, for example (nothing to do with links)

so, bring back getLinkId() anyway pleeease xD ... it can still be very useful for may things

thanks for your patience

JAndrassy commented 3 years ago

server.accept() returns a client (new TCP connection) only once. the AdvancedChatServer example shows how to manage individual TCP connections. additionally every concurrent TCP connection has unique remoteIP+remotePort.

I added a new example PagerServer. this is an example which doesn't care about individual clients.

JAndrassy commented 3 years ago

you can add

  uint8_t getLinkId() {
    if (stream)
      return stream->getLinkId();
    return WIFIESPAT_NO_LINK;
  }

in WiFiClient.h to get the linkId. but again, it can return the same linkId for two different connections

atesin commented 3 years ago

linkId is not a hash, what's the problem with two different sequential connections coming through the same link?

JAndrassy commented 3 years ago

linkId is not a hash, what's the problem with two different sequential connections coming through the same link?

it depends how you want to use it. if as index in an array of WiFiClients you could overwrite a client which still has data to read even if it is closed in AT firmware

atesin commented 3 years ago

you are right

anyway, taking some precautions, with getLinkId() the read clients process can be greatly optimized, take the AdvancedChatServer.ino main loop for example (last commit prior 2020-10-08)

according your are talking, currently when a new client connects and there are "zombie" clients and no more free slots, you have to hold it until the next main loop(), because you try to store the new client BEFORE eat whole data from zombie client and release slot... this could be avoided with a bigger array

or with getLinkId() maybe the process could be rewritten this way, ensuring data processing and releasing slots before store a new client, just in 1 single clients loop:

could this be done?, would be better? (noticing the users)