Domanda

Stiamo scrivendo un client e un server per fare (quello che pensavo fosse) piuttosto semplici comunicazioni di rete. clienti mulitple si collegano al server che poi si suppone per inviare i dati di nuovo a tutti gli altri clienti.

Il server si trova proprio di un ciclo di blocco select attesa per il traffico, e quando si tratta, invia i dati ad altri clienti. Questo sembra funzionare bene.

Il problema è il client. In risposta a una lettura, ma a volte vuole fare una scrittura.

Tuttavia, ho trovato che se uso:

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

Il mio codice si bloccherà fino a quando ci sono nuovi dati da leggere. Ma a volte (in modo asincrono, da un altro thread) dovrò nuovi dati da scrivere sul filo di comunicazione di rete. Quindi, voglio che il mio scegliere di svegliarsi periodicamente e mi permetta di verificare se ci sono dati da scrivere, come:

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

Ho provato a fissare la modalità di selezione (o ridicolmente breve timeout) con il polling:

// 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);

, ma hanno trovato che quindi poi cliente non viene mai i dati in entrata.

Ho anche provato a fissare il fd socket non bloccante, come:

fcntl(sockfd, F_SETFL, O_NONBLOCK);

, ma questo non risolve il problema:

  1. se il mio cliente non ha select() struct timeval, la lettura di opere di dati, ma non è mai sblocca di farmi guardare per i dati scrivibili.
  2. se il mio cliente select() ha una timeval per farlo sondaggio, allora non segnala che non ci sono dati in arrivo da leggere, e il mio congela app pensando non c'è alcuna connessione di rete fatta (nonostante il fatto che tutte le altre chiamate di funzione sono riusciti )

Tutti gli indicatori a tutti come per quello che ho potuto fare male? Non è possibile fare lettura e scrittura nella stessa presa (non riesco a credere che per essere vero).

(EDIT: La risposta corretta, e cosa che mi sono ricordato sul server ma non sul client, è quello di avere un secondo fd_set, e copiare master_list prima di ogni chiamata per selezionare ():

// 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
}

)

È stato utile?

Soluzione

Tutto sembra bene, tranne la linea in cui si fa if (FD_SET(sockfd, &master_list)). Ho una struttura di codice molto simile e ho usato FD_ISSET. Si suppone per verificare se la lista è impostato, non impostare nuovamente. Oltre a questo, non vedo niente altro.

Modifica. Inoltre, ho il seguente per il timeout:

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

forse c'è un problema se si imposta a 0 (come ti sembra di fare?)

Edit2. Mi sono ricordato mi sono imbattuto in uno strano problema quando non si stava schiarendo la lettura impostata dopo il select chiuso e prima di entrare di nuovo. Dovevo fare qualcosa di simile:

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

prima stavo entrando select. Non riesco a ricordare il motivo per cui però.

Altri suggerimenti

Mi sembra di ricordare un trucco sulla creazione e la condivisione di un descrittore di file di lettura / scrittura tra il filo della rete e il filo conduttore che viene aggiunto ai descrittori nella chiamata di selezione. Questo fd ha un byte scritta ad essa da parte del thread principale quando si ha qualcosa da inviare. La scrittura si sveglia filo di rete dalla chiamata di selezione e il filo di rete viene poi prende i dati da un buffer condiviso e lo scrive alla rete poi tornare a dormire in selezione.

Scusate se questo è un po 'vago e privo di codice ... e la mia memoria non può essere corretto .. così gli altri possono avere per guidare l'utente ulteriormente.

Non vedo niente di sbagliato con il codice, quindi dovrebbe funzionare. Se non è possibile farlo funzionare, un modo di lavorare intorno ad esso sarebbe quello di creare un tubo per essere utilizzato da tuo thread di lettura e il filo che prepara le cose per la scrittura, e aggiungere fine lettura del tubo per il set select. Poi, quando l'altro thread ha preparato i dati di scrivere, semplicemente invia qualcosa sul tubo, il thread di lettura viene svegliato dal select, e può quindi fare la scrittura. A seconda di come spesso ci sono dati da leggere o scrivere, questo potrebbe anche essere più efficiente.

2 filettature devono essere in grado di lavorare con la stessa presa alla volta, in modo che il filo principale dovrebbe essere in grado di scrivere su un client mentre l'altro dorme selezionare attesa per i dati in ingresso. Questo naturalmente presuppone che entrambi i fili hanno accesso alla lista di clienti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top