Bonjour,
J'utilise le Shield Ethernet 2 Arduino et j'ai constaté le comportement suivant, grâce à WireShark qui permet d'analyser les échanges TCP/IP
Le code suivant marche bien, la réponse envoyée au client est faite dans un unique packet TCP/IP
En revanche, si je construit la réponse "en plusieurs fois", c'est à dire avec plusieurs instructions client.write(), j'ai un packet pour chaque instruction write(), même si elles sont effectuées immédiatement l'une après l'autre.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 void loop() { // Looking for client connections and send them responses client = server.available(); if (client) { //while (client.connected()) { if (client.connected()) { if (client.available()) { Built_Response(); // It's important to use a buffer, to send all data in a single packet // WARNING : Buffer_HTTP can contains null \0 bytes, so we have to use client.write instead of client.print client.write(Buffer_HTTP+Buffer_HTTP_Offset, Buffer_HTTP_Stop-Buffer_HTTP_Offset); } } delay(1); // give the web browser time to receive the data client.stop(); // close the connection: } }
Pourquoi c'est ennuyeux ?
Hé bien faire plusieurs instructions write() au lieu d'une seule permet de ne pas avoir un gros buffer en RAM.
Il est stupide de devoir stocker en RAM un gros buffer dont une partie non négligeable sert à écrire des chaines fixes en PROGMEM
J'avais un code qui économisait la RAM, mais je pouvait me retrouver avec des paquets TCP/IP n'ayant qu'un seul octet de charge utile, sachant qu'un paquets possède 54 octets de "header" (je parle des "headers" du paquet TCP/IP, rien à voir avec le "header" HTTP) donc l'efficacité de la communication réseau est médiocre
Comment faire, avec le W5500, pour "remplir" d'abord son buffer de données et ensuite seulement lui donner le feu vert pour envoyer un packet ?
Voici le bout de code "write" de ma version de W5100.cpp
Voici le bout de code "write" de ma version de socket.cpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 uint16_t W5100Class::write(uint16_t addr, const uint8_t *buf, uint16_t len) { uint8_t cmd[8]; setSS(); if (addr < 0x100) { // common registers 00nn cmd[0] = 0; cmd[1] = addr & 0xFF; cmd[2] = 0x04; } else if (addr < 0x8000) { // socket registers 10nn, 11nn, 12nn, 13nn, etc cmd[0] = 0; cmd[1] = addr & 0xFF; cmd[2] = ((addr >> 3) & 0xE0) | 0x0C; } else if (addr < 0xC000) { // transmit buffers 8000-87FF, 8800-8FFF, 9000-97FF, etc // 10## #nnn nnnn nnnn cmd[0] = addr >> 8; cmd[1] = addr & 0xFF; #if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1 cmd[2] = 0x14; // 16K buffers #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2 cmd[2] = ((addr >> 8) & 0x20) | 0x14; // 8K buffers #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4 cmd[2] = ((addr >> 7) & 0x60) | 0x14; // 4K buffers #else cmd[2] = ((addr >> 6) & 0xE0) | 0x14; // 2K buffers #endif } else { // receive buffers cmd[0] = addr >> 8; cmd[1] = addr & 0xFF; #if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1 cmd[2] = 0x1C; // 16K buffers #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2 cmd[2] = ((addr >> 8) & 0x20) | 0x1C; // 8K buffers #elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4 cmd[2] = ((addr >> 7) & 0x60) | 0x1C; // 4K buffers #else cmd[2] = ((addr >> 6) & 0xE0) | 0x1C; // 2K buffers #endif } if (len <= 5) { for (uint8_t i=0; i < len; i++) { cmd[i + 3] = buf[i]; } SPI.transfer(cmd, len + 3); } else { SPI.transfer(cmd, 3); #ifdef SPI_HAS_TRANSFER_BUF SPI.transfer(buf, NULL, len); #else // TODO: copy 8 bytes at a time to cmd[] and block transfer for (uint16_t i=0; i < len; i++) { SPI.transfer(buf[i]); } #endif } resetSS(); return len; }
Voici le bout de code "write" de ma version de EthernetServer.cpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 static void write_data(uint8_t s, uint16_t data_offset, const uint8_t *data, uint16_t len) { uint16_t ptr = W5100.readSnTX_WR(s); ptr += data_offset; uint16_t offset = ptr & W5100.SMASK; uint16_t dstAddr = offset + W5100.SBASE(s); if (W5100.hasOffsetAddressMapping() || offset + len <= W5100.SSIZE) { W5100.write(dstAddr, data, len); } else { // Wrap around circular buffer uint16_t size = W5100.SSIZE - offset; W5100.write(dstAddr, data, size); W5100.write(W5100.SBASE(s), data + size, len - size); } ptr += len; W5100.writeSnTX_WR(s, ptr); }
Merci
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 size_t RLucas_EthernetServer::write(uint8_t b) { return write(&b, 1); } size_t RLucas_EthernetServer::write(const uint8_t *buffer, size_t size) { available(); for (uint8_t i=0; i < MAX_SOCK_NUM; i++) { //for (uint8_t i=0; i < maxindex; i++) { if (server_port[i] == _port) { if (Ethernet.socketStatus(i) == SnSR::ESTABLISHED) { Ethernet.socketSend(i, buffer, size); } } } return size; }
A bientôt
Partager