Pourquoi des messages UDP particuliers sont-ils toujours déposés en dessous d’une taille de mémoire tampon particulière?

3 messages différents sont envoyés au même port à des débits différents:

Taille du message (octets) Envoyé à chaque vitesse de transmission
Haute 232 10 ms 100Hz
Moyenne 148 20ms 50Hz
Bas 20 60 ms 16.6Hz

Je ne peux traiter qu’un message toutes les 6 ms environ .
Seul fileté. Blocage de lecture.


Une situation étrange se produit, et je n’ai pas d’explication.
Lorsque je règle mon tampon de réception sur 4,799 octets, tous mes messages à faible vitesse sont supprimés.
Je vois peut-être un ou deux être traités, puis rien.

Lorsque je règle mon tampon de réception sur 4,800 (ou plus!), Il semble que tous les messages à faible vitesse commencent à être traités. Je vois environ 16/17 par seconde.


Cela a été observé de manière constante. L’application qui envoie les paquets est toujours démarrée avant l’application destinataire. L’application récepsortingce a toujours un long délai après la création des sockets et avant le début du traitement. Ainsi, la mémoire tampon est toujours pleine au début du traitement et ce n’est pas la même mémoire tampon de démarrage à chaque fois qu’un test est exécuté. En effet, le socket étant créé après que l’expéditeur a déjà envoyé des messages, le destinataire peut commencer à écouter au milieu d’un cycle d’envoi.

Pourquoi l’augmentation de la taille de la mémoire tampon reçue d’un octet entraîne-t-elle un changement considérable dans le traitement des messages à faible vitesse?

J’ai construit un tableau pour mieux visualiser le traitement attendu:
entrez la description de l'image ici

Au fur et à mesure que certains de ces messages sont traités, davantage de messages sont probablement mis en queue au lieu d’être supprimés.

Néanmoins, je m’attendrais à ce qu’un tampon de 4,799 octets se comporte de la même manière que 4,800 octets.

Cependant, ce n’est pas ce que j’ai observé.


Je pense que le problème est lié au fait que les messages à faible vitesse sont envoyés en même temps que les deux autres messages. Il est toujours reçu après les messages haute / moyenne vitesse. (Cela a été confirmé par WireShark).

Par exemple, en supposant que la mémoire tampon était vide pour commencer, il est clair que le message à faible vitesse aurait besoin d’être mis en queue plus longtemps que les autres messages.
* 1 message toutes les 6 ms correspond à environ 5 messages toutes les 30 ms. entrez la description de l'image ici

Cela n’explique toujours pas la taille du tampon.

Nous utilisons VxWorks et utilisons sockLib, une implémentation des sockets Berkeley. Voici un extrait de notre création de socket:
SOCKET_BUFFER_SIZE est ce que je change.

 struct sockaddr_in tSocketAddress; // Socket address int nSocketAddressSize = sizeof(struct sockaddr_in); // Size of socket address structure int nSocketOption = 0; // Already created if (*ptParameters->m_pnIDReference != 0) return FALSE; // Create UDP socket if ((*ptParameters->m_pnIDReference = socket(AF_INET, SOCK_DGRAM, 0)) == ERROR) { // Error CreateSocketMessage(ptParameters, "CreateSocket: Socket create failed with error."); // Not successful return FALSE; } // Valid local address if (ptParameters->m_szLocalIPAddress != SOCKET_ADDRESS_NONE_STRING && ptParameters->m_usLocalPort != 0) { // Set up the local parameters/port bzero((char*)&tSocketAddress, nSocketAddressSize); tSocketAddress.sin_len = (u_char)nSocketAddressSize; tSocketAddress.sin_family = AF_INET; tSocketAddress.sin_port = htons(ptParameters->m_usLocalPort); // Check for any address if (strcmp(ptParameters->m_szLocalIPAddress, SOCKET_ADDRESS_ANY_STRING) == 0) tSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY); else { // Convert IP address for binding if ((tSocketAddress.sin_addr.s_addr = inet_addr(ptParameters->m_szLocalIPAddress)) == ERROR) { // Error CreateSocketMessage(ptParameters, "Unknown IP address."); // Cleanup socket close(*ptParameters->m_pnIDReference); *ptParameters->m_pnIDReference = ERROR; // Not successful return FALSE; } } // Bind the socket to the local address if (bind(*ptParameters->m_pnIDReference, (struct sockaddr *)&tSocketAddress, nSocketAddressSize) == ERROR) { // Error CreateSocketMessage(ptParameters, "Socket bind failed."); // Cleanup socket close(*ptParameters->m_pnIDReference); *ptParameters->m_pnIDReference = ERROR; // Not successful return FALSE; } } // Receive socket if (ptParameters->m_eType == SOCKTYPE_RECEIVE || ptParameters->m_eType == SOCKTYPE_RECEIVE_AND_TRANSMIT) { // Set the receive buffer size nSocketOption = SOCKET_BUFFER_SIZE; if (setsockopt(*ptParameters->m_pnIDReference, SOL_SOCKET, SO_RCVBUF, (char *)&nSocketOption, sizeof(nSocketOption)) == ERROR) { // Error CreateSocketMessage(ptParameters, "Socket buffer size set failed."); // Cleanup socket close(*ptParameters->m_pnIDReference); *ptParameters->m_pnIDReference = ERROR; // Not successful return FALSE; } } 

et le socket reçoit ce qui est appelé dans une boucle infinie:
* La taille du tampon est vraiment assez grande

 int SocketReceive(int nSocketIndex, char *pBuffer, int nBufferLength) { int nBytesReceived = 0; char szError[256]; // Invalid index or socket if (nSocketIndex = SOCKET_COUNT || g_pnSocketIDs[nSocketIndex] == 0) { sprintf(szError,"SocketReceive: Invalid socket (%d) or ID (%d)", nSocketIndex, g_pnSocketIDs[nSocketIndex]); perror(szError); return -1; } // Invalid buffer length if (nBufferLength == 0) { perror("SocketReceive: zero buffer length"); return 0; } // Send data nBytesReceived = recv(g_pnSocketIDs[nSocketIndex], pBuffer, nBufferLength, 0); // Error in receiving if (nBytesReceived == ERROR) { // Create error ssortingng sprintf(szError, "SocketReceive: Data Receive Failure:  ", errno); // Set error message perror(szError); // Return error return ERROR; } // Bytes received return nBytesReceived; } 

Pourquoi la hausse de la taille de la mémoire tampon à 4 800 entraîne-t-elle une lecture réussie et cohérente des messages à faible vitesse?

La réponse de base à la question de savoir pourquoi une taille SO_RCVBUF de 4799 entraîne la perte de messages à faible vitesse et une taille de 4800 fonctionne correctement est que, avec le mélange des paquets UDP entrant, le taux auquel ils entrent, le taux de vous mbuff les paquets entrants et le dimensionnement des numéros de mbuff et de cluster dans votre kernel vxWorks permet un débit de stack réseau suffisant pour que les messages à faible vitesse ne soient pas ignorés avec la taille la plus grande.

La description de l’option SO_SNDBUF dans la page de manuel setsockopt() à l’URL http://www.vxdev.com/docs/vx55man/vxworks/ref/sockLib.html#setsockopt mentionnée ci-dessus indique la taille spécifiée et l’effet sur l’utilisation de mbuff :

L’effet de la définition de la taille maximale des mémoires tampons (pour SO_SNDBUF et SO_RCVBUF, décrit ci-dessous) ne consiste pas en réalité à allouer les mbufs du pool mbuf. Au lieu de cela, l’effet consiste à définir la limite supérieure dans la structure de données du protocole, qui est utilisée ultérieurement pour limiter la quantité d’allocation mbuf.

Les paquets UDP sont des unités discrètes. Si vous envoyez 10 paquets de taille 232, la mémoire contiguë ne constitue pas 2320 octets de données. Au lieu de cela, il y a 10 mémoires tampons dans la stack réseau, car UDP correspond à des paquets discrets et TCP à un stream continu d’octets.

Voir Comment régler la mise en mémoire tampon du réseau dans VxWorks 5.4? sur le site Web de la communauté DDS qui propose une discussion sur l’interdépendance du mélange de tailles de mbuff et de grappes de réseaux.

Voir Comment résoudre un problème avec les tampons VxWorks? sur le site Web de la communauté DDS.

Voir ce PDF d’une présentation de diapositive, Un nouvel outil pour étudier l’épuisement de la stack réseau dans VxWorks à partir de 2004, qui décrit l’utilisation de divers outils tels que mBufShow et inetStatShow pour voir ce qui se passe dans la stack réseau.

Sans parsing détaillée de chaque implémentation de stack réseau le long du chemin, vos messages UDP sont envoyés, il est presque impossible d’énoncer le comportement résultant.

Les implémentations UDP sont autorisées à supprimer n’importe quel paquet à leur propre discrétion. Cela se produit généralement quand une stack en arrive à la conclusion qu’il faudrait abandonner des paquets pour pouvoir en recevoir de nouveaux. Il n’y a pas d’exigence formelle que les paquets déposés soient les plus anciens ou les plus récents reçus. Il se peut également qu’une certaine classe de taille soit plus affectée en raison de stratégies de gestion de la mémoire interne.

Parmi les stacks IP impliquées, la plus intéressante est celle de la machine récepsortingce.

Vous obtiendrez certainement une meilleure expérience de réception si vous changez le côté de la réception pour avoir un tampon de réception qui prendra plusieurs secondes plein de messages attendus. Je commencerais par au moins 10k.

Le «changement» observé dans le comportement lorsque vous passez de 4 799 à 4 800 peut découler de la possibilité ultérieure de permettre à l’un des petits messages d’être reçu avant qu’il ne soit redéployé, alors que sa taille réduite le fait légèrement plus tôt. Si l’application destinataire est suffisamment rapide pour lire le message en attente, vous recevrez de petits messages dans un cas et aucun message dans l’autre.