Description
I ran into this problem by sending binary packets with an interval of 250ms.
Anything larger than ~2900bytes would fail. My client is windows and has probably the dreaded 200ms ACK delay, hence my rate of 250ms.
I looked into WebSockets.cpp code and in particular the one that uses headerToPayload (since I am using this feature to merge header and payload in same buffer):
I made a small modification that now allows me to send larger buffers (>2900bytes) with a frequency of 250ms (with Windows client):
~around line 250 in WebSockets.cpp
//if(client->tcp->write(&payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) {
if(client_tcp_write_multi(client,&payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) {
ret = false;
}
Note that I replaced client->tcp->write() with a function client_tcp_write_multi that basically makes multiple calls to client->tcp->write() if the later only returns a partial result. In current code v2.0.6 it is required for the payload to be sent in a single client->tcp->write() - was it assumed that tcp->write() will handle this multi-write internally and why it's not doing so in my case and from where comes the ~2900 limit ?:
size_t client_tcp_write_multi(WSclient_t * client, uint8_t * data, int32_t data_len){
int32_t total_bytes_written = 0;
int32_t bytes_written;
do{
bytes_written = client->tcp->write(&data[0], data_len);
data_len -= bytes_written;
data += bytes_written;
total_bytes_written += bytes_written;
Serial.printf("tcp_bytes_written:%d\n",bytes_written);
}while(bytes_written > 0 && data_len > 0 );
return total_bytes_written;
}
I hope you can review this and implement as a permanent feature in your code (or at least in those critical calls likely to be called by people who need speed in sending).
You can see from output of my code that indeed it is possible to make multiple calls to
client->tcp->write() and rescue sendFrame() from failing.
seq:22 dt_us:205401 ms:121371 ms_delta:251 r:1 dl:6500
tcp_bytes_written:2920
tcp_bytes_written:2920
tcp_bytes_written:664
seq:23 dt_us:205191 ms:121622 ms_delta:251 r:1 dl:6500
tcp_bytes_written:2920
tcp_bytes_written:2920
tcp_bytes_written:664
and snippet from my code:
void loop() {
uint32_t ms_delta = millis() - ms_last;
if(batch_run && current_client_num>=0 && ms_delta > interval_ms){
ms_last = millis();
memcpy(data , &sequence, 1);
memcpy(data + 1, &ms_last, 4);
uint32_t t0 = micros();
bool result = ws.sendBIN(current_client_num,buf,data_len ,1 /*headerToPayload*/);
DBG_SERIAL.printf("seq:%u dt_us:%u ms:%u ms_delta:%u r:%u dl:%u\n",sequence, micros()-t0 , ms_last, ms_delta ,result,data_len);
sequence++;
}
ws.loop();
}
Interesting phenomena: Without the patch code above , even if sendFrame() would return 0, I would still get some data on client side, but with every other call to .sendBin() skipped , so not all packets make it to the client.