2010-07-26 2 views
6

Dans mon programme, il y a un thread (thread de réception) qui est chargé de recevoir des demandes d'un socket TCP et il y a beaucoup de threads (threads de travail) chargés du traitement demandes Une fois qu'une demande est traitée, je dois envoyer une réponse via TCP.Comment annuler en attente dans select() sous Windows

Et voici une question. Je voudrais envoyer des données TCP dans le même thread que j'utilise pour recevoir des données. Ce fil après avoir reçu des données attend généralement de nouvelles données dans select(). Ainsi, une fois qu'un thread de travail a fini de traiter une requête et de mettre une réponse dans la file d'attente de sortie, il doit signaler au thread de réception qu'il y a des données à envoyer. Le problème est que je ne sais pas comment annuler l'attente en select() afin de sortir de l'attente et d'appeler send(). Ou dois-je utiliser un autre thread uniquement pour envoyer des données via TCP?

Mise à jour

MSalters, Artyom merci pour vous répondre!

MSalters, après avoir lu votre réponse, j'ai trouvé ce site: Winsock 2 I/O Methods et lire environ WSAWaitForMultipleEvents(). Mon programme doit en fait fonctionner à la fois sur HP-UX et Windows J'ai finalement décidé d'utiliser l'approche qui avait été suggérée par Artyom.

+0

Pourquoi utilisez-vous 'select()' pour écrire? Habituellement, 'select()' attend simplement les données reçues, et vous pouvez 'envoyer()' des données en utilisant les mêmes socket (s) pendant ce temps. – ereOn

+0

Non, je n'utilise pas select pour l'écriture. Est-ce que j'ai dit ca? –

+0

@skwllsp: Je l'ai supposé puisque vous semblez vouloir annuler 'select()' pour écrire sur votre socket, ce qui n'est pas nécessaire. Mais peut-être que j'ai mal compris le tout, mon anglais n'est pas exactement natif. – ereOn

Répondre

13

Vous devez utiliser quelque chose de similaire au truc du tuyau de sécurité, mais dans votre cas, vous devez utiliser une paire de sockets TCP connectées.

  1. Créez une paire de sockets.
  2. Ajoutez-en un à la sélection et attendez-le également
  3. Avertissez en écrivant à un autre socket à partir d'autres threads.
  4. Select est immédiatement waken-up comme l'une des prises de courant est lisible, lit toutes les données dans cette prise spéciale et vérifier toutes les données dans les files d'attente pour envoyer/recv

Comment créer deux prises sous Windows ?

inline void pair(SOCKET fds[2]) 
{ 
    struct sockaddr_in inaddr; 
    struct sockaddr addr; 
    SOCKET lst=::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP); 
    memset(&inaddr, 0, sizeof(inaddr)); 
    memset(&addr, 0, sizeof(addr)); 
    inaddr.sin_family = AF_INET; 
    inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 
    inaddr.sin_port = 0; 
    int yes=1; 
    setsockopt(lst,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes)); 
    bind(lst,(struct sockaddr *)&inaddr,sizeof(inaddr)); 
    listen(lst,1); 
    int len=sizeof(inaddr); 
    getsockname(lst, &addr,&len); 
    fds[0]=::socket(AF_INET, SOCK_STREAM,0); 
    connect(fds[0],&addr,len); 
    fds[1]=accept(lst,0,0); 
    closesocket(lst); 
} 

Bien sûr, certaines vérifications doivent être ajoutées pour les valeurs de retour.

+0

'(char *) & oui, sizeof (oui)' ?? nouveau pour moi :) – sarnold

+0

@sarnold Contrairement à POSIX setsockopt, Windows 1 reçoit la valeur 'char const * 'au lieu de' void const * 'comme sur les plateformes POSIX. – Artyom

+2

Vous n'avez pas vraiment besoin d'une paire de sockets, vous pouvez utiliser un seul socket UDP, "connecté" à lui-même. – Hasturkun

7

select n'est pas l'API native pour Windows. La manière native est WSAWaitForMultipleEvents. Si vous l'utilisez pour créer une attente d'alerte, vous pouvez utiliser QueueUserAPC pour indiquer au thread en attente d'envoyer des données. (Cela peut également signifier que vous n'avez pas à implémenter votre propre file d'attente de sortie)

+1

Ce que vous suggérez est d'utiliser IOCP qui ont de nombreux inconvénients et ... à moins de gérer 10000 connexions, il est préférable de les éviter. – Artyom

+0

Eh bien, les IOCP sont un moyen de sortir d'un WSAWaitForMultipleEvents. Vous pouvez également ajouter un événement à la liste des objets attendus et signaler l'événement lorsqu'il y a des données à envoyer. Je ne me souviens pas des problèmes avec la combinaison de WaitForMultipleEvents/QueueUserAPC, cependant, c'est pourquoi je l'ai suggéré. – MSalters

+1

Artyom, quels sont alors les 'nombreux inconvénients' de l'IOCP? Je ne dis pas que c'est la solution à la question initiale ou à cette question en particulier; Je suis juste curieux de savoir ce que vous pensez est difficile sur la communication basée sur IOCP .. –

0

Le modèle typique est que l'opérateur doit gérer sa propre écriture. Y a-t-il une raison pour laquelle vous voulez envoyer tous les output-IO via select thread? Si vous êtes sûr de ce modèle, vous pouvez demander à vos employés de renvoyer des données au thread principal à l'aide des descripteurs de fichier (pipe(2)) et d'ajouter simplement ces descripteurs à votre appel select(). Et, si vous êtes particulièrement sûr de ne pas utiliser de canaux pour renvoyer des données à votre processus maître, l'appel select vous permet de spécifier un délai d'attente. Vous pouvez attendre pendant la vérification de vos threads de travail et appeler régulièrement le select pour savoir de quels connecteurs TCP vous voulez lire.

+0

Windows n'a pas de tuyaux sélectionnables – Artyom

+0

Ah. J'avais supposé que Windows avait implémenté la spécification POSIX de près. Merci pour la note. – sarnold

+1

"Windows a implémenté les spécifications POSIX de près" Ohhhhh, tu m'as fait rire. :) – Artyom

0

Une autre solution rapide & consiste à ajouter des sockets localhost à l'ensemble. Utilisez maintenant ces sockets comme files d'attente de communication inter-thread. Chaque thread de travail envoie simplement quelque chose à son socket, qui finit sur le socket correspondant dans votre thread de réception. Cela réveille le select(), et votre thread de réception peut alors renvoyer le message sur la socket sortante appropriée.

+0

En fait, WSAWaitForMultipleEvents semble être plus ou moins sutable mais je dois lire quelques manuels sur son utilisation avant de prendre une décision –

4

Voir aussi ce message: How to signal select() to return immediately?

Pour unix, utilisez un tuyau anonyme. Pour Windows: Le déblocage peut être obtenu en ajoutant un socket de datagramme fictif (non lié) à fd_set, puis en le fermant. Pour sécuriser ce thread, utilisez QueueUserAPC:

La seule façon que j'ai trouvée pour rendre cette sécurité multithread est de fermer et de recréer le socket dans le même thread que l'instruction select. Bien sûr, c'est difficile si le thread bloque sur le select. Et puis vient dans l'appel de fenêtres QueueUserAPC. Lorsque Windows bloque dans l'instruction select, le thread peut gérer les appels de procédure asynchrones. Vous pouvez planifier cela à partir d'un thread différent en utilisant QueueUserAPC. Windows interrompt le select, exécute votre fonction dans le même thread et continue avec l'instruction select. Vous pouvez maintenant dans votre méthode APC fermer le socket et le recréer. Fil sécurisé garanti et vous ne perdrez jamais un signal.

Questions connexes