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

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:
	}
}
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.

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

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 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
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);
}
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
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;
}
Merci

A bientôt