Question

Nous écrivons un client et un serveur pour le faire (ce que je pensais) des communications réseau assez simple. clients mulitple se connectent au serveur qui est alors censé envoyer les données à tous les autres clients.

Le serveur se trouve juste dans une boucle de select de blocage en attente pour le trafic, et quand il vient, envoie les données aux autres clients. Cela semble fonctionner très bien.

Le problème est le client. En réponse à une lecture, il voudra parfois faire une écriture.

Cependant, j'ai trouvé que si je l'utilise:

 rv = select(fdmax + 1, &master_list, NULL, NULL, NULL);

Mon code bloquera jusqu'à ce que de nouvelles données sont à lire. Mais parfois (de manière asynchrone, d'un autre fil) Je vais avoir de nouvelles données à écrire sur le fil de communication réseau. Donc, je veux que mon sélection pour se réveiller périodiquement et laissez-moi vérifier s'il y a des données à écrire, comme:

if (select(....) != -1)
{
  if (FD_SET(sockfd, &master_list))
     // handle data or disconnect
  else
     // look for data to write and write() / send() those.
}

J'ai essayé le réglage de la sélection en mode sondage (ou ridiculement court délais d'attente) avec:

// master list contains the sockfd from the getaddrinfo/socket/connect seq
struct timeval t;
memset(&t, 0, sizeof t);
rv = select(fdmax + 1, &master_list, NULL, NULL, &t);

mais ont constaté que puis ensuite client obtient jamais de données entrantes.

J'ai aussi essayé de placer la prise fd être non-blocage, comme:

fcntl(sockfd, F_SETFL, O_NONBLOCK);

mais cela ne résout pas le problème:

  1. si mon select() client n'a pas struct timeval, la lecture des œuvres de données, mais il ne débloque de me laisser chercher des données inscriptible.
  2. si mon select() client a un timeval pour l'obtenir au scrutin, il signale jamais qu'il ya des données entrantes à lire, et mes gels app pensant qu'il n'y a pas de connexion réseau fait (malgré le fait que tous les autres appels de fonction ont réussi )

Les pointeurs du tout sur ce que je pourrais faire mal? Est-il pas possible de faire lecture-écriture sur le même socket (je ne peux pas croire que pour être vrai).

(EDIT: La bonne réponse, et que je me suis souvenu chose sur le serveur mais pas sur le client, est d'avoir une seconde fd_set et copier master_list avant chaque appel pour sélectionner ():

// declare and FD_ZERO read_fds:
// put sockfd in master_list

while (1)
{
   read_fds = master_list;
   select(...);

   if (FD_ISSET(read_fds))
     ....
   else
     // sleep or otherwise don't hog cpu resources
}

)

Était-ce utile?

La solution

Tout semble bien, à l'exception de la ligne où vous if (FD_SET(sockfd, &master_list)). J'ai une structure de code très similaire et je FD_ISSET. Vous êtes censé vérifier si la liste est définie, pas le régler à nouveau. A part cela, je ne vois rien d'autre.

Modifier. Aussi, j'ai ce qui suit pour le délai d'attente:

timeval listening_timeout;
listening_timeout.tv_sec = timeout_in_seconds;
listening_timeout.tv_usec = 0;

peut-être il y a un problème si vous définissez à 0 (comme vous semblez le faire?)

Edit2. Je me suis souvenu que je suis tombé sur un problème étrange quand je ne Clearing la lecture fixé après la sélection et Exited avant d'entrer à nouveau. Je devais faire quelque chose comme:

FD_ZERO(&sockfd);
FD_SET(sockfd, &rd);

avant que je suis entré dans select. Je ne me souviens pas pourquoi si.

Autres conseils

Je crois me rappeler un truc sur la création et le partage d'un descripteur de fichier de lecture / écriture entre le fil de réseau et le thread principal qui est ajouté aux descripteurs dans l'appel select. Ce fd a un octet écrit par le thread principal quand il a quelque chose à envoyer. L'écriture se réveille fil de réseau à partir de l'appel select et le fil de réseau saisit alors les données à partir d'un tampon partagé et écrit sur le réseau puis revenir dormir dans la sélection.

Désolé si c'est un peu vague et manque de code ... et ma mémoire peut être incorrect .. pour que les autres peuvent avoir pour vous guider plus loin.

Je ne vois rien de mal avec votre code, donc il devrait fonctionner. Si vous ne pouvez pas le faire fonctionner, d'une façon de travailler autour d'elle serait de créer un tuyau pour être utilisé par votre fil de lecture et le fil qui prépare des choses pour l'écriture, et ajouter la fin de la lecture du tuyau à votre jeu de select. Puis, quand l'autre thread a préparé des données à écrire, il envoie juste quelque chose sur le tuyau, votre fil de lecture se réveiller de la select, et il peut alors faire de l'écriture. Selon la fréquence il y a des données à lire ou à écrire, cela pourrait aussi être plus efficace.

2 fils doivent être en mesure de travailler avec la même prise à la fois, de sorte que votre thread principal devrait être en mesure d'écrire à un client tandis que l'autre dort dans l'attente de sélection pour les données entrantes. Bien sûr, cela suppose que les deux fils ont accès à la liste des clients.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top