2010-03-09 6 views
3

J'essaie de convertir du code existant pour utiliser les sockets asio tcp de boost au lieu de notre implémentation actuelle. Je suis en mesure d'obtenir un exemple très similaire (of a chat client/server) à partir du site boost, mais quand je tente de mettre le code dans mon propre programme, il cesse de fonctionner.boost :: asio tcp async_read ne retourne jamais

Ce que je fais:

  1. Démarrer un processus serveur
  2. Le processus serveur fait une prise de courant et l'utilise pour écouter (avec un tcp :: accepteur) pour les connexions TCP sur un port (10010 par exemple)
  3. lancer un processus client
  4. Demandez le processus client crée une connexion socket au port du serveur
  5. Lorsque le serveur voit un client se connecte, il commence à écouter les données (avec async_read) sur le socket et crée un autre socket vide pour écouter une autre connexion TCP sur le port
  6. Lorsque le client voit que le serveur s'est connecté, il envoie 100 octets de données (avec async_write) et attend que le socket lui dise le envoyer est terminée ... quand cela arrive, il imprime un message et ferme
  7. lorsque le serveur est notifié que son a des données qui a été lu, il imprime un message et ferme

de toute évidence, je grandement réduit ce code de ce que j'essaie de mettre en œuvre, c'est aussi petit que je pourrais faire quelque chose qui reproduit le problème. Je cours sur windows et ai un fichier visuel de solution de studio que vous pouvez get. Il y a des fuites de mémoire, des problèmes de sécurité des threads, etc., mais c'est parce que je retire des choses du code existant, alors ne vous inquiétez pas pour eux.

Quoi qu'il en soit, voici l'en-tête de fichiers avec des choses courantes, un serveur et un client.

Connection.hpp:


#ifndef CONNECTION_HPP 
#define CONNECTION_HPP 
#include 
#include 
#include 

class ConnectionTransfer 
{ 
public: 
    ConnectionTransfer(char* buffer, unsigned int size) : 
     buffer_(buffer), size_(size) { 
    } 
    virtual ~ConnectionTransfer(void){} 

    char* GetBuffer(){return buffer_;} 
    unsigned int GetSize(){return size_;} 

    virtual void CallbackForFinished() = 0; 

protected: 
    char* buffer_; 
    unsigned int size_; 
}; 

class ConnectionTransferInProgress 
{ 
public: 
    ConnectionTransferInProgress(ConnectionTransfer* ct): 
     ct_(ct) 
    {} 
    ~ConnectionTransferInProgress(void){} 

    void operator()(const boost::system::error_code& error){Other(error);} 
    void Other(const boost::system::error_code& error){ 
     if(!error) 
     ct_->CallbackForFinished(); 
    } 
private: 
    ConnectionTransfer* ct_; 
}; 

class Connection 
{ 
public: 
    Connection(boost::asio::io_service& io_service): 
    sock_(io_service) 
    {} 
    ~Connection(void){} 
    void AsyncSend(ConnectionTransfer* ct){ 
     ConnectionTransferInProgress tip(ct); 
     //sock_->async_send(boost::asio::buffer(ct->GetBuffer(), 
     // static_cast(ct->GetSize())), tip); 
     boost::asio::async_write(sock_, boost::asio::buffer(ct->GetBuffer(), 
     static_cast(ct->GetSize())), boost::bind(
     &ConnectionTransferInProgress::Other, tip, boost::asio::placeholders::error)); 
    } 
    void AsyncReceive(ConnectionTransfer* ct){ 
     ConnectionTransferInProgress tip(ct); 
     //sock_->async_receive(boost::asio::buffer(ct->GetBuffer(), 
     // static_cast(ct->GetSize())), tip); 
     boost::asio::async_read(sock_, boost::asio::buffer(ct->GetBuffer(), 
     static_cast(ct->GetSize())), boost::bind(
     &ConnectionTransferInProgress::Other, tip, boost::asio::placeholders::error)); 
    } 

    boost::asio::ip::tcp::socket& GetSocket(){return sock_;} 
private: 
    boost::asio::ip::tcp::socket sock_; 
}; 
#endif //CONNECTION_HPP 

BoostConnectionClient.cpp:


#include "Connection.hpp" 

#include 
#include 
#include 
#include 

using namespace boost::asio::ip; 

bool connected; 
bool gotTransfer; 

class FakeTransfer : public ConnectionTransfer 
{ 
public: 
    FakeTransfer(char* buffer, unsigned int size) : ConnectionTransfer(buffer, size) 
    { 
    } 
    void CallbackForFinished() 
    { 
     gotTransfer = true; 
    } 
}; 

void ConnectHandler(const boost::system::error_code& error) 
{ 
    if(!error) 
     connected = true; 
} 

int main(int argc, char* argv[]) 
{ 
    connected = false; 
    gotTransfer = false; 

    boost::asio::io_service io_service; 

    Connection* conn = new Connection(io_service); 

    tcp::endpoint ep(address::from_string("127.0.0.1"), 10011); 
    conn->GetSocket().async_connect(ep, ConnectHandler); 

    boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service)); 

    while(!connected) 
    { 
     boost::this_thread::sleep(boost::posix_time::millisec(1)); 
    } 
    std::cout (angle brackets here) "Connected\n"; 

    char data[100]; 
    FakeTransfer* ft = new FakeTransfer(data, 100); 
    conn->AsyncReceive(ft); 

    while(!gotTransfer) 
    { 
     boost::this_thread::sleep(boost::posix_time::millisec(1)); 
    } 

    std::cout (angle brackets here) "Done\n"; 

    return 0; 
} 

BoostConnectionServer.cpp:


#include "Connection.hpp" 

#include 
#include 
#include 
#include 

using namespace boost::asio::ip; 

Connection* conn1; 
bool conn1Done; 
bool gotTransfer; 
Connection* conn2; 

class FakeAcceptor 
{ 
public: 
    FakeAcceptor(boost::asio::io_service& io_service, const tcp::endpoint& endpoint) 
     : 
     io_service_(io_service), 
     acceptor_(io_service, endpoint) 
    { 
     conn1 = new Connection(io_service_); 
     acceptor_.async_accept(conn1->GetSocket(), 
     boost::bind(&FakeAcceptor::HandleAccept, this, conn1, 
     boost::asio::placeholders::error)); 
    } 
    void HandleAccept(Connection* conn, const boost::system::error_code& error) 
    { 
     if(conn == conn1) 
     conn1Done = true; 
     conn2 = new Connection(io_service_); 
     acceptor_.async_accept(conn2->GetSocket(), 
     boost::bind(&FakeAcceptor::HandleAccept, this, conn2, 
     boost::asio::placeholders::error)); 
    } 
    boost::asio::io_service& io_service_; 
    tcp::acceptor acceptor_; 
}; 

class FakeTransfer : public ConnectionTransfer 
{ 
public: 
    FakeTransfer(char* buffer, unsigned int size) : ConnectionTransfer(buffer, size) 
    { 
    } 
    void CallbackForFinished() 
    { 
     gotTransfer = true; 
    } 
}; 

int main(int argc, char* argv[]) 
{ 
    boost::asio::io_service io_service; 
    conn1Done = false; 
    gotTransfer = false; 
    tcp::endpoint endpoint(tcp::v4(), 10011); 
    FakeAcceptor fa(io_service, endpoint); 
    boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service)); 

    while(!conn1Done) 
    { 
     boost::this_thread::sleep(boost::posix_time::millisec(1)); 
    } 
    std::cout (angle brackets here) "Accepted incoming connection\n"; 

    char data[100]; 
    FakeTransfer* ft = new FakeTransfer(data, 100); 
    conn1->AsyncReceive(ft); 

    while(!gotTransfer) 
    { 
     boost::this_thread::sleep(boost::posix_time::millisec(1)); 
    } 
    std::cout (angle brackets here) "Success!\n"; 
    return 0; 
} 

J'ai cherché un peu, mais ne l'ai pas eu beaucoup de chance. Autant que je sache, je fais presque exactement correspondre l'échantillon, donc ce doit être quelque chose de petit que je néglige.

Merci!

+0

De quel côté ne l'async jamais lu retour? – ergosys

Répondre

2

Dans votre code client, votre fonction de rappel ConnectHandler() ne fait que définir une valeur, puis revient, sans envoyer de travail supplémentaire au service io_service. À ce stade, cette opération async_connect() est le seul travail associé au io_service; donc quand ConnectHandler() revient, il n'y a plus de travail associé au io_service. Ainsi, l'appel du thread d'arrière-plan à io_service.run() renvoie et le thread se termine.

Une option possible serait d'appeler conn->AsyncReceive() de l'intérieur ConnectHandler(), de sorte que le async_read() est appelé avant le retour ConnectHandler() et donc l'appel du fil d'arrière-plan io_service.run() ne reviendra pas.

Une autre option, celle plus triviale, serait d'instancier un io_service :: instance de travail avant de créer votre fil à io_service appeler :: run (techniquement, vous pouvez le faire à tout point avant de l'io_service.run() appel retour):

... 
// some point in the main() method, prior to creating the background thread 
boost::asio::io_service::work work(io_service) 
... 

Ceci est documenté dans la documentation io_service:

Arrêt du io_service de manquer de travail

Certaines applications peuvent avoir besoin d'empêcher l'exécution de l'appel run() d'un objet io_service lorsqu'il n'y a plus de travail à effectuer. Par exemple, le service io_service peut être exécuté dans un thread d'arrière-plan lancé avant les opérations asynchrones de l'application. La course() appel peut être maintenu en cours d'exécution en créant un objet de type io_service :: travail:

http://www.boost.org/doc/libs/1_43_0/doc/html/boost_asio/reference/io_service.html

Questions connexes