2017-06-22 3 views
1

Ich bin ein AspNet-Programmierer mit 57 Jahren. Weil ich der Einzige war, der am Anfang mit C++ gearbeitet hat, baten mich meine Chefs, einen Kunden zu bedienen, der einen Kommunikationsagenten mit sehr spezifischen Eigenschaften braucht. Es kann als Daemon auf mehreren Plattformen ausgeführt werden und gleichzeitig Client und Server sein. Ich weiß nicht genug, aber ich muss das Problem lösen und habe eine Chance in der Boost/Asio-Bibliothek gefunden.Server und Client zur gleichen Zeit mit Boost-Asio

Ich bin neu in Boost-Asio und lese die Dokumentation, die ich einen Server und einen TCP-Socket-Client erstellt, der Nachrichten perfekt und bidirektional, Vollduplex austauscht.

Ich las mehrere Posts, wo sie nach den gleichen Dingen verlangten, die ich möchte, aber alle Antworten schlugen Vollduplex vor, als ob das bedeutete, einen Client und einen Server im gleichen Programm zu haben. Und es ist nicht. Die Definition von Vollduplex bezieht sich auf die Fähigkeit, von derselben Verbindung zu schreiben und zu lesen, und jede TCP-Verbindung ist standardmäßig Vollduplex.

Ich muss dafür sorgen, dass zwei Programme Verbindungen annehmen können, die vom anderen initiiert wurden. Es wird keine dauerhafte Verbindung zwischen den beiden Programmen geben. Manchmal fragt einer von ihnen nach einer Verbindung und zu anderen Zeiten wird der andere diese Anfrage machen und beide müssen zuhören, die Verbindung akzeptieren, einige Nachrichten austauschen und die Verbindung beenden, bis eine neue Anfrage gemacht wird.

Der Server, den ich tat, scheint in den Prozess des Abhörens auf den Port zu hängen, um zu sehen, ob eine Verbindung kommt und ich kann nicht mit dem Prozess fortfahren, einen Socket erstellen und eine Verbindung mit dem anderen anfordern können Programm. Ich brauche Threads, aber ich weiß nicht genug über sie.

Es ist möglich?

Wie gesagt, ich bin neu bei Boost/Asio und habe versucht, einige Dokumente von Threads und Coroutines zu folgen. Dann habe ich den Client-Codes in einem Verfahren und der Server in einem anderen .:

int main(int argc, char* argv[]) 
{ 
    try 
    { 
     boost::thread t1(&server_agent); 
     boost::thread t2(&client_agent); 

     // wait 
     t1.join(); 
     t2.join(); 
     return 0;  
    } 
    catch (std::exception& e) 
    { 
     std::cerr << "Exception: " << e.what() << "\n"; 
    }  
    return 0; 
} 

und zwei Koroutinen:

void client_agent() { 
    parameters param; 
    param.load(); 

    boost::asio::io_service io_service1; 
    tcp::resolver resolver(io_service1); 
    char port[5]; 
    _itoa(param.getNrPortaServComunic(), port, 10); 
    auto endpoint_iterator = resolver.resolve({ param.getIPServComunicPrincipal(), port }); 
    std::list<client> clients; 
    client c(io_service1, endpoint_iterator, param); 

    while (true) 
    { 
     BOOL enviada = FALSE; 
     while (true) { 
      if (!enviada) { 
       std::cout << "sending a message\n"; 
       int nr = 110; 
       message msg(nr, param); 
       c.write(msg); 
       enviada = TRUE; 
      } 
     } 
    } 

    c.close(); 
} 

void server_agent() { 

    parameters param; 
    param.load(); 

    boost::asio::io_service io_service1; 
    std::list<server> servers; 
    tcp::endpoint endpoint(tcp::v4(), param.getNrPortaAgenteServ()); 
    servers.emplace_back(io_service1, endpoint); 
    io_service1.run(); 
} 

Ich benutzen einen Port auf Client-Endpunkt und ander Port Server-Endpunkt. Ist es richtig? Erforderlich?

Es sieht aus, als würde es funktionieren. Jede der Methoden läuft gleichzeitig, aber dann bekomme ich einen Fehler bei der Thread-Zuweisung bei io_service1.run (letzte Zeile der Methode server_agent):

boost :: exception_detail :: clone_impl> am Speicherort 0x0118C61C.

Irgendwelche Vorschläge?

+1

Wie für den Titel, ich bin mir ziemlich sicher, dass Sie eine Vollduplex-Server/Client-Client/Server-Implementierung mit Boost Asio tun können. Aktivieren Sie einfach einen 'boost :: acceptor 'für beide Prozesse und eine Clientverbindung. Behandeln Sie einfach alles asynchron. –

+0

Danke. Ich werde diesen Ansatz versuchen. Es scheint die einzige Möglichkeit zu sein, nicht in einer Schleife stecken zu bleiben. – Neumann

Antwort

0

Sie beschreiben eine UDP-Client/Server-Anwendung. Aber Ihre Implementierung wird zwangsläufig fehlschlagen. Stellen Sie sich einen Asio Server oder Client vor, der immer in einem einzigen Thread läuft.

Der folgende Code ist nur, damit Sie eine Idee bekommen. Ich habe nicht versucht, es zu kompilieren. Der Client ist sehr ähnlich, aber möglicherweise einen Sendepuffer, abhängig von der App, offensichtlich.

Dies ist eine verkürzte Version, damit Sie die Idee bekommen. In einer letzten Anwendung möchten Sie Empfangs-Timeouts und Ähnliches hinzufügen. Die gleichen Prinzipien gelten für TCP-Server mit dem hinzugefügten async_listen-Aufruf. Verbundene Sockets können in shared_ptr gespeichert und von den Lambdas erfasst werden, werden fast magisch zerstören.

Server ist im Grunde das gleiche, außer es gibt kein konstantes Lesen geht. Wenn Sie sowohl den Server als auch den Client im selben Prozess ausführen, können Sie sich darauf verlassen, dass run() wegen des Servers durchläuft. Wenn nicht, müssten Sie für jede Verbindung run() aufrufen. run() würde am Ende des Austauschs enden.

using namespace boost::asio; // Or whichever way you like to shorten names 

class Server 
{ 
    public: 
    Server(io_service& ios) : ios_(ios) {} 

    void Start() 
    { 
     // create socket 
     // Start listening 
     Read(); 
    } 

    void Read() 
    { 
     rxBuffer.resize(1024) 
     s_.async_receive_from(
      buffer(rxBuffer), 
      remoteEndpoint_, 
      [this](error_code ec, size_t n) 
     { 
      OnReceive(ec, n); // could be virtual, if done this way 
     }); 
    } 

    void OnReceive(error_code ec, size_t n) 
    { 
     rxBuffer_.resize(n); 
     if (ec) 
     { 
      // error ... stops listen loop 
      return; 
     } 

     // grab data, put in txBuffer_ 
     Read(); 
     s_.async_send_to(
      buffer(txBuffer_), 
      remoteEndpoint_, 
      [this, msg](error_code ec, size_t n) 
     { 
      OnTransmitDone(ec, n); 
     }); 
    } 

    void OnTransmitDone(error_code ec, size_t n) 
    { 
    // check for error? 
    txBuffer_.clear(); 
    } 

    protected: 
    io_service& ios_; 
    ip::udp::socket s_; 
    ip::udp::endpoint remoteEndpoint_; // the other's address/port 
    std::vector<char> rxBuffer_;  // could be any data type you like 
    std::vector<char> txBuffer_;  // idem All access is in one thread, so only 
             // one needed for simple ask/respond ops. 
}; 

int main() 
{ 
    io_service ios; 
    Server server(ios); // could have both server and client run on same thread 
         // on same io service this way. 

    Server.Start(); 

    ios_run(); 
    // or std::thread ioThread([&](){ ios_.run(); }); 
    return 0; 
} 
+0

Danke, Michaël Roy, Sie lassen es einfach aussehen, ich werde versuchen, Ihren Vorschlag zu verwenden. – Neumann

+0

Ich habe kopiert/eingefügt von einer funktionierenden App, die ich letzten Monat gemacht habe, habe ich einige Reste ([diese, msg]) entdeckt. Es ist viel einfacher, als mit select() oder epoll/iocp umgehen zu müssen. Eine komplett funktionierende Embedded Client + Server App für openwrt + windows wurde in 10 Tagen erstellt. Watchdog-Timer und Fehlerbehandlung/Socket-Reset, etc. waren sehr einfach zu diesem Modell hinzuzufügen, und nicht wirklich die Lesbarkeit zu verletzen. Leistung und CPU-Nutzungszahlen sind großartig. Wenn Sie eine externe Datenquelle haben, möchten Sie eine TX-Queue hinzufügen, aber das war relativ einfach hinzuzufügen, sobald das grundlegende Skelett vorhanden war. A ++ für boost.asio. –

Verwandte Themen