2017-10-05 4 views
0

Je sais que UDP est sans connexion, mais pourrais-je générer un nouveau thread pour gérer les nouveaux clients UDP qui se connectent à mon serveur?Je peux générer un nouveau thread pour gérer une nouvelle "connexion" UDP?

Je suis en train de faire essentiellement:

void *handle_connection(void *param) 
{ 
    // read data 
    ret = recvfrom(..,0,..); 
} 


// Wait for connection using MSG_PEEK 
ret = recvfrom(.., MSG_PEEK,..); 

// New thread to handle client connection 
pthread_t conn_thread; 
ret = pthread_create(..., handle_connection); 

est-il un moyen de affermira une « connexion » UDP pour chaque client et bifurquer un nouveau thread pour gérer comme il est fait par OpenSSL lors de l'utilisation DTLS? Avec OpenSSL, je peux utiliser DTLS_listen() pour attendre qu'un client UDP se connecte, puis démarrer un nouveau thread, passer le socket UDP à ce thread et utiliser les opérations habituelles de lecture/écriture. Pour que cela fonctionne, vous devez faire un couple d'appels supplémentaires bind() et connect() sur les sockets udp.

J'ai essayé la même approche ici et j'ai fini par ouvrir un nouveau thread pour chaque datagramme reçu du client sur le serveur.

+0

Pouvez-vous? Pourquoi pas? Vous pouvez générer de nouveaux threads pour faire ce que vous voulez. –

+0

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. –

+0

@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

Répondre

2

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:

  1. 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.
  2. recvfrom vous donne le prochain datagramme reçu et l'adresse de à laquelle il a été envoyé.
  3. Transmettez le datagramme initial et l'adresse à un nouveau thread.
  4. 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.
  5. 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))