Imansible rilevare la sincronizzazione sincrona di C ++ :: asio :: ip :: tcp :: socket chiusa

Sto usando boost::asio per creare una connessione socket TCP sincrona a un’applicazione server TCP node.js che viene eseguita sullo stesso computer. Sto usando Embarcadero RAD studio XE4 su Windows che costruisce un’applicazione a 64 bit che usa la versione 1.50 integrata di Embarcadero.

Le cose funzionano bene tranne quando il server TCP node.js si spegne. In questo caso, l’applicazione client C ++ non rileva la disconnessione quando leggo dal socket. Tuttavia, rileva la disconnessione quando scrivo sul socket.

Il mio codice attuale è stato scritto dopo aver cercato di dare un senso alla documentazione di boost e una varietà di risposte qui su SO. La parte di lettura del codice è la seguente (ho omesso il controllo del codice di errore per compattezza)

 boost::system::error_code ec; boost::asio::io_service io_service; boost::asio::ip::tcp::socket s(io_service); boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"),m_port); ec = s.connect(ep,ec); std::size_t bytes_available = s.available(ec); std::vector data(bytes_available,0); size_t read_len = boost::asio::read(s, boost::asio::buffer(data),boost::asio::transfer_at_least(bytes_available),ec); if ((boost::asio::error::eof == ec) || (boost::asio::error::connection_reset == ec)) { // disconnection break; } 

Questo non è un grande sistema in quanto è in esecuzione in un thread all’interno di un ciclo e esegue il polling dei dati costantemente fino all’arresto del programma. Normalmente non eseguirò un read() su un socket quando non ci sono dati lì ma lo faccio in questo caso poiché tutta la documentazione mi porta a credere che la disconnessione del socket venga rilevata solo quando si esegue una lettura o una scrittura sul socket. Il problema è che il codice sopra non sta semplicemente rilevando la disconnessione quando l’applicazione node.js si arresta. Se scrivo lo rilevo (la parte di scrittura del codice sta usando gli stessi errori boost :: asio :: error come read per il rilevamento) ma non durante la lettura.

Ovviamente non posso eseguire una lettura per quantità di byte maggiori di quelle disponibili o il mio thread si bloccherà e non sarò in grado di eseguire le scritture più avanti nel mio ciclo di thread.

Mi manca un altro codice di errore boost specifico per rilevare la condizione di errore? O è un problema specifico con la lettura della lunghezza zero. Se questo è il caso, ci sono altre opzioni disponibili per me?

Attualmente sto ricevendo il server node.js per scrivere un messaggio specifico quando si collega al socket quando si spegne, che sto rilevando e quindi chiudendo il client stesso. Tuttavia, questo è un po ‘un trucco e preferirei un modo pulito per rilevare la disconnessione, se ansible.

In generale, le funzioni read() di Boost.Asio ritornano quando:

  • Il buffer è pieno.
  • La condizione completa è stata soddisfatta.
  • C’è stato un errore.

Non è specificato l’ordine in cui vengono verificate queste condizioni. Tuttavia, l’ultima volta che ho esaminato l’implementazione, Boost.Asio ha trattato operazioni che leggevano zero byte su uno stream come no-op prima di tentare di leggere dal socket. Quindi, non verrà osservato un errore di fine file, perché il buffer di dimensione 0 è considerato pieno.

Sebbene l’utilizzo di operazioni asincrone possa fornire risultati e scalabilità migliori, il rilevamento di una disconnessione in modalità non bloccante può ancora essere eseguito con operazioni sincrone. Per impostazione predefinita, le operazioni sincrone stanno bloccando, ma questo comportamento può essere controllato tramite la funzione socket::non_blocking() . La documentazione afferma:

Se true , le operazioni sincrone del socket falliranno con boost::asio::error::would_block se non sono in grado di eseguire immediatamente l’operazione richiesta. Se false , le operazioni sincrone verranno bloccate fino al completamento.

Pertanto, se le operazioni sincrone sono impostate su non bloccare e l’operazione di lettura tenta di leggere un minimo di 1 byte, è ansible osservare una disconnessione in modo non bloccante.


Ecco un esempio completo che dimostra un’operazione di read() sincrona non bloccante che rileva una disconnessione. Per limitare i messaggi di stampa, ho optato per eseguire una sospensione quando l’operazione sarebbe stata bloccata (ovvero ha una connessione ma non sono disponibili dati da leggere).

 #include  #include  #include  #include  #include  int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: \n"; return 1; } // Create socket and connet to local port. namespace ip = boost::asio::ip; boost::asio::io_service io_service; ip::tcp::socket socket(io_service); socket.connect(ip::tcp::endpoint( ip::address::from_string("127.0.0.1"), std::atoi(argv[1]))); // By setting the socket to non-blocking, synchronous operations will // fail with boost::asio::error::would_block if they cannot immediately // perform the requested operation. socket.non_blocking(true); // Synchronously read data. std::vector data; boost::system::error_code ec; for (;;) { // Resize the buffer based on the amount of bytes available to be read. // Guarantee that the buffer is at least 1 byte, as Boost.Asio treats // zero byte read operations as no-ops. data.resize(std::max(1, socket.available(ec))); // Read all available data. std::size_t bytes_transferred = boost::asio::read(socket, boost::asio::buffer(data), ec); // If no data is available, then continue to next iteration. if (bytes_transferred == 0 && ec == boost::asio::error::would_block) { std::cout << "no data available" << std::endl; boost::this_thread::sleep_for(boost::chrono::seconds(3)); continue; } std::cout << "Read: " << bytes_transferred << " -- "; if (bytes_transferred) { std::cout.write(&data[0], bytes_transferred); std::cout << " -- "; } std::cout << ec.message() << std::endl; // On error, such as a disconnect, exit the loop. if (ec && ec != boost::asio::error::would_block) { break; } } } 

Il seguente output è stato prodotto collegando il programma di esempio a un server che ha scritto "test", "altri test", quindi ha chiuso la connessione:

 no data available Read: 4 -- test -- Success no data available Read: 12 -- more testing -- Success Read: 0 -- End of file