Je pense que cela peut être fait. Mais je ne suis pas sûr de ce que sont les garanties.
L'idée de base est:
- Créer socket serveur, définissez
SO_REUSEADDR
sur elle, se lient au port du serveur , et attendez de recevoir des données à ce sujet.
recvfrom
vous donne le prochain datagramme reçu et l'adresse de à laquelle il a été envoyé.
- Transmettez le datagramme initial et l'adresse à un nouveau thread.
- Dans le nouveau thread, créez un nouveau socket (à nouveau avec
SO_REUSEADDR
et liez-le). Maintenant connect
le nouveau socket à l'adresse de l'homologue.
- Retour à l'étape 2
L'idée ici est que vous gardez toujours une prise « non connecté ». Lorsque vous obtenez un nouveau pair, vous créez un nouveau socket connecté à cet homologue. Quand un socket est spécifiquement connecté à un homologue distant, le noyau remettra un datagramme entrant de cet homologue à ce socket. Sinon, il va livrer le datagramme au socket non connecté. La chose dont je ne suis pas sûr à 100% est: Est-ce que le système d'exploitation garantit de livrer un datagramme ultérieur uniquement à la prise connectée au lieu de la prise non connectée? Il est logique que cela fonctionne de cette façon et il semble fonctionner sur ma boîte Linux - voir le code python3 ci-dessous (peut utiliser nc -u localhost 9999
comme client).
#!/usr/bin/env python3
from threading import Thread
import socket
def client_thread(addr, msg):
# Create a new socket and connect it to the peer.
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', 9999)) # Maybe omit -- see below
sock.connect(addr)
# Maybe set SO_RCVTIMEO to cause timeout on receive and
# cause client thread to bail?
while True:
sock.send(msg)
msg = sock.recv(1024)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', 9999))
threads = []
while True:
# Receive datagram on unconnected socket
msg, addr = sock.recvfrom(1024)
# Send connected socket and datagram
th = Thread(target=client_thread, args=(addr, msg))
th.start()
threads.append(th)
Vous probablement avoir un moyen de tuer les sous-fils éventuellement afin qu'ils ne traînent pas toujours si votre client disparaît. Sinon, vous pouvez omettre de lier la nouvelle socket au client_thread
, ce qui obligera le noyau à choisir un port inutilisé pour l'adresse locale dans le nouveau socket, mais le client devra alors faire attention à l'adresse à partir de laquelle il reçu la réponse initiale et diriger les futurs datagrammes vers cette adresse. Cela permettrait d'accomplir la même chose et à bien des égards, d'être plus propre.
Le côté client pourrait être quelque chose comme ça (encore une fois en utilisant python pour la démonstration):
#!/usr/bin/env python3
import socket
# Initial request sent to well-known port
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
addr = ('localhost', 9999)
for n in range(5):
print('Send to {}'.format(addr))
sock.sendto(b'Hello', addr)
response, addr = sock.recvfrom(1024)
# Subsequent requests sent to whoever responded to last request
print('Got response {} from {}'.format(response, addr))
Pouvez-vous? Pourquoi pas? Vous pouvez générer de nouveaux threads pour faire ce que vous voulez. –
Vous pouvez; Cependant, il n'est pas clair qu'il y ait un avantage à le faire. Un seul thread peut gérer n'importe quel nombre de clients UDP en utilisant un seul socket. –
@JeremyFriesner Vous pouvez dans OpenSSL lors de l'emballage de votre socket autour d'objets BIO et SSL ... Donc je me demandais si la même chose pourrait être fait ici. – user2584587