Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Redirection UDP vers TCP avec socat

6 réponses
Avatar
Bruno Tréguier
Bonjour,

Dans une application interne, je reçois des paquets UDP de différentes
provenances, sur un serveur prédéterminé. Ces paquets UDP arrivent sur
des ports fixés par mes soins (1 port différent par provenance).

Plusieurs personnes ou applications peuvent être "clientes" de ces
données. Pour différentes raisons, j'ai choisi de le faire en TCP.

Chaque donnée UDP est donc, via un petit programme en Perl, mise à
disposition d'éventuels clients, en TCP, sur le même port. Au démarrage,
j'ouvre autant de ports TCP en écoute que de ports UDP sur lesquels je
peux recevoir une donnée. Quand une donnée arrive sur un port UDP x sur
mon serveur, elle est donc renvoyée à tous les clients connectés à
l'instant t sur le port TCP x (la communication n'est
qu'unidirectionnelle, il n'y a pas d'envoi de données dans l'autre
sens). La plage de ports surveillée est bien entendu limitée (quelques
dizaines).

Je gère donc (par port) une liste de clients afin de ne renvoyer la
donnée qu'à ceux qui sont connectés. Pour détecter le départ d'un
client, je tente une écriture sur la socket TCP, et si je reçois un
SIGPIPE, je passe par une procédure qui va enlever proprement le client
concerné de mes "abonnés" du moment.

Ca marche pas mal, mais de temps en temps j'ai des plantages dont je
n'arrive pas à déterminer la cause, surtout qu'il peut très bien se
passer 1 semaine ou plus sans souci, et après cela je vais être
enquiquiné plusieurs fois dans la même journée.

J'ai l'impression que mon code de détection des départs de clients n'est
pas fiable, mais plutôt que de me replonger dedans et de perdre trop de
temps, je me demande si je n'ai pas une option plus simple à l'aide de
l'utilitaire "socat".

Voici la ligne de commande à laquelle je suis arrivé pour l'instant (ici
pour le port 5200, par exemple)

socat -u UDP-RECV:5200 TCP-LISTEN:5200,fork

Cela marche presque, sauf que socat ne semble faire que des connexions 1
<-> 1 (même avec l'option -u qui indique qu'on ne fera que de la lecture
sur la 1ère socket, et que de l'écriture sur la 2ème). Donc, malgré le
"fork", seule l'un des clients TCP à un instant donné recevra la donnée
reçue sur la socket UDP. Il ne semble pas y avoir de moyen de faire en
sorte que tous les clients TCP en aient une copie (connexion 1 <-> n,
donc). Bon, en même temps ce n'est pas très étonnant, socat, bien
qu'ayant d'énormes possibilités, n'a pas été fait pour ça à la base.

Cela dit, si l'un d'entre vous a une astuce, je suis preneur. ;-)

Merci !

--
Bruno Tréguier

6 réponses

Avatar
Nicolas George
Bruno Tréguier
, dans le message <io9jtl$n6$, a écrit :
Chaque donnée UDP est donc, via un petit programme en Perl, mise à
disposition d'éventuels clients, en TCP, sur le même port.



Comment transmet-il les bornes de paquets ?

Je gère donc (par port) une liste de clients afin de ne renvoyer la
donnée qu'à ceux qui sont connectés. Pour détecter le départ d'un
client, je tente une écriture sur la socket TCP, et si je reçois un
SIGPIPE, je passe par une procédure qui va enlever proprement le client
concerné de mes "abonnés" du moment.



Et si un client est toujours là mais ne lit pas les données que tu lui
envoies ?

Ca marche pas mal, mais de temps en temps j'ai des plantages dont je
n'arrive pas à déterminer la cause, surtout qu'il peut très bien se
passer 1 semaine ou plus sans souci, et après cela je vais être
enquiquiné plusieurs fois dans la même journée.



Dans ce cas, c'est assez facile d'attacher le programme dans un débugger, ne
serait-ce que strace, pour voir exactement ce qui se passe.
Avatar
Bruno Tréguier
Le 15/04/2011 à 16:35, Nicolas George a écrit :

Comment transmet-il les bornes de paquets ?



Je n'ai pas bien saisi le but de la question, j'ai dû mal m'expliquer,
donc voici un peu plus de détails sur l'origine des données:

J'ai des appareils qui fournissent des données (une suite de caractères
codés en ASCII) en RS232. Ces données sont envoyées sur des
convertisseurs RS232/Ethernet. Ces derniers ont plusieurs modes de
fonctionnement (serveur TCP, client TCP, "client" UDP) mais pour des
raisons hors contexte par rapport à la problématique exposée ici, c'est
le mode UDP qui a été choisi.

Chaque convertisseur est configuré pour qu'un datagramme UDP soit émis
dans 3 cas:
1) réception d'un caractère Line Feed (LF)
2) réception d'un nombre de caractères donné (80 de mémoire, mais de
toute façon, le cas 1) arrivera avant normalement, chaque paquet de
données faisant environ 40 caractères ;-)
3) nombre de caractères > 0 mais plus de données reçues depuis plus de 1
seconde.

Je ne sais pas si c'est la réponse que tu attendais. Quoi qu'il en soit,
le "serveur UDP" en face est en attente dans un select(2) et dès qu'un
descripteur de socket est prêt en lecture, signe qu'au moins 1 socket
UDP a reçu quelque chose, les données sont lues et retransmises sur la
série de sockets TCP correspondantes (autant que de clients connectés
pour la socket UDP considérée, donc).


Je gère donc (par port) une liste de clients afin de ne renvoyer la
donnée qu'à ceux qui sont connectés. Pour détecter le départ d'un
client, je tente une écriture sur la socket TCP, et si je reçois un
SIGPIPE, je passe par une procédure qui va enlever proprement le client
concerné de mes "abonnés" du moment.



Et si un client est toujours là mais ne lit pas les données que tu lui
envoies ?



Dans ce cas c'est le mécanisme de flow control de TCP qui joue: pour
chaque socket TCP, je vérifie aussi que je peux écrire, si ce n'est pas
le cas, je ne le fais pas, ce qui sous-entend qu'un client TCP "peu
attentif" risque de perdre des données, mais bon, c'est le jeu, je ne
peux pas bufferiser à l'infini...


Ca marche pas mal, mais de temps en temps j'ai des plantages dont je
n'arrive pas à déterminer la cause, surtout qu'il peut très bien se
passer 1 semaine ou plus sans souci, et après cela je vais être
enquiquiné plusieurs fois dans la même journée.



Dans ce cas, c'est assez facile d'attacher le programme dans un débugger, ne
serait-ce que strace, pour voir exactement ce qui se passe.



strace n'est pas suffisant, il semble que le bug ne se situe pas au
niveau des appels système, mais plutôt quelque part au niveau de la
gestion de la mémoire. S'il le faut je ferai un débogage "en règle",
bien entendu, mais mon message d'aujourd'hui avait juste pour but de
tenter de trouver une solution pour laisser le "sale boulot" à socat, et
de simplement "scripter" autour.

Merci pour ta réponse !

Cordialement,

--
Bruno Tréguier
Avatar
Nicolas George
Bruno Tréguier
, dans le message <ioa61r$77i$, a écrit :
Comment transmet-il les bornes de paquets ?


Je n'ai pas bien saisi le but de la question, j'ai dû mal m'expliquer,



En UDP, un deux write de 40 octets donnent un résultat différent d'un write
de 80, alors qu'en TCP, c'est potentiellement indiscernable : selon ne
scheduling et les latences réseau, les deux write de 40 peuvent être
réassemblés en un seul segment.

Donc si on forwarde bêtement les données lues en UDP vers du TCP, on perd de
l'information : les limites de paquets. Cette information peut être
indispensable ou pas, selon le type de données convoyées.

La suite de ton message semble indiquer que l'information est redondante.

Dans ce cas c'est le mécanisme de flow control de TCP qui joue: pour
chaque socket TCP, je vérifie aussi que je peux écrire, si ce n'est pas
le cas, je ne le fais pas, ce qui sous-entend qu'un client TCP "peu
attentif" risque de perdre des données, mais bon, c'est le jeu, je ne
peux pas bufferiser à l'infini...



C'est bien ce qu'il faut faire. Mais du coup, si tu en es là, tu peux aussi
bien te permettre de détecter proprement la déconnexion des clients.

strace n'est pas suffisant, il semble que le bug ne se situe pas au
niveau des appels système, mais plutôt quelque part au niveau de la
gestion de la mémoire.



J'ai un peu du mal à voir comment c'est possible.
Avatar
Bruno Tréguier
Le 15/04/2011 à 23:18, Nicolas George a écrit :
En UDP, un deux write de 40 octets donnent un résultat différent d'un write
de 80, alors qu'en TCP, c'est potentiellement indiscernable : selon ne
scheduling et les latences réseau, les deux write de 40 peuvent être
réassemblés en un seul segment.



Tout à fait d'accord, mais:

Donc si on forwarde bêtement les données lues en UDP vers du TCP, on perd de
l'information : les limites de paquets. Cette information peut être
indispensable ou pas, selon le type de données convoyées.

La suite de ton message semble indiquer que l'information est redondante.



Ouala. Les limites de mes messages, tels qu'ils sont interprétés par la
suite de la chaîne, ce sont les caractères LF. Donc que les infos
transitent en 1, 2 ou 40 datagrammes, je m'en tape. ;-)


Dans ce cas c'est le mécanisme de flow control de TCP qui joue: pour
chaque socket TCP, je vérifie aussi que je peux écrire, si ce n'est pas
le cas, je ne le fais pas, ce qui sous-entend qu'un client TCP "peu
attentif" risque de perdre des données, mais bon, c'est le jeu, je ne
peux pas bufferiser à l'infini...



C'est bien ce qu'il faut faire. Mais du coup, si tu en es là, tu peux aussi
bien te permettre de détecter proprement la déconnexion des clients.



Ben... C'est ce que je pense déjà faire, en fait... Mais comme les
opérations ne sont pas atomiques, entre le moment où je teste la
possibilité d'écriture, et le moment où j'écris, il peut se passer des
trucs, mon déroutement sur SIGPIPE est là pour ça.


strace n'est pas suffisant, il semble que le bug ne se situe pas au
niveau des appels système, mais plutôt quelque part au niveau de la
gestion de la mémoire.



J'ai un peu du mal à voir comment c'est possible.



Pff, y'a des jours où on ferait mieux de se coucher plus tôt, ou de
faire moins de trucs en même temps. ;-) Je voulais parler d'un problème
de gestion des sockets (et pas de mémoire): j'en ouvre et j'en ferme pas
mal, en fonction des clients qui se connectent ou se déconnectent. Il
faut que je revoie ma logique à ce niveau... ou que je trouve comment
utiliser correctement socat pour faire du 1 <-> n !

Allez, bonne nuit !

--
Bruno Tréguier
Avatar
Bruno Tréguier
Le 15/04/2011 à 23:58, Bruno Tréguier a écrit :
Ouala. Les limites de mes messages, tels qu'ils sont interprétés par la
suite de la chaîne, ce sont les caractères LF. Donc que les infos
transitent en 1, 2 ou 40 datagrammes, je m'en tape. ;-)



Petite précision (sans rapport direct avec mon souci, mais juste pour
expliquer certains choix): oui, je sais, l'ordre des datagrammes n'est
pas garanti, ni leur arrivée d'ailleurs, mais d'une part, avec 1
datagramme par seconde, le risque d'inversion est assez limité, et
d'autre part, les messages sont horodatés et leur format est contrôlé à
l'arrivée (quand-même :-) ), ce qui fait qu'une erreur dans un message
entraînerait tout simplement la poubellisation de celui-ci, et qu'une
inversion simple entre 2 messages complets n'aurait aucune conséquence.
Un taux de perte inférieur à quelques centaines de paquets par jour
reste tout à fait tolérable pour le reste de la chaîne de traitement (il
s'agit de résultats de mesure, à raison de 1 par seconde).

Re-bonne nuit !

--
Bruno Tréguier
Avatar
Erwan David
Bruno Tréguier écrivait :

Le 15/04/2011 à 23:18, Nicolas George a écrit :
En UDP, un deux write de 40 octets donnent un résultat différent d'un write
de 80, alors qu'en TCP, c'est potentiellement indiscernable : selon ne
scheduling et les latences réseau, les deux write de 40 peuvent être
réassemblés en un seul segment.



Tout à fait d'accord, mais:

Donc si on forwarde bêtement les données lues en UDP vers du TCP, on perd de
l'information : les limites de paquets. Cette information peut être
indispensable ou pas, selon le type de données convoyées.

La suite de ton message semble indiquer que l'information est redondante.



Ouala. Les limites de mes messages, tels qu'ils sont interprétés par
la suite de la chaîne, ce sont les caractères LF. Donc que les infos
transitent en 1, 2 ou 40 datagrammes, je m'en tape. ;-)



et s'ils transitent en plusieurs datagrammes qui arriventdans le
désordre, tu t'en fous aussi ?

--
Le travail n'est pas une bonne chose. Si ça l'était,
les riches l'auraient accaparé