2017-06-22 84 views
1

我是一位57岁的AspNet程序员。因为我是唯一一个在一开始就用C++工作的人,所以我的老板们要求我为一个需要具有非常特定特征的沟通代理的客户提供服务。它可以在多个平台上作为守护程序运行,并且有时可以是客户端和服务器。我不够了解,但我必须解决问题,并在Boost/Asio图书馆找到机会。服务器和客户端同时使用Boost-Asio

我是Boost-Asio的新手,并阅读文档,我创建了一个服务器和一个TCP套接字客户端,可以完美地交换消息并双向全双工。

我读了几篇文章,他们询问我想要的东西,但所有答案都提示全双工,就好像这意味着在同一个程序中有一个客户端和一台服务器。事实并非如此。全双工的定义是指从同一连接写入和读取的能力,默认情况下每个TCP连接都是全双工。

我需要让两个程序可以接受另一个发起的连接。这两个方案之间不会有永久性的联系。有时候他们中的一个会要求连接,而另一个则会提出这个请求,并且都需要监听,接受连接,交换一些消息并终止连接,直到发出新的请求。

我所做的服务器似乎陷入了侦听端口的过程中,看看是否有连接进入,并且我无法继续进程以便能够创建套接字并请求与其他端口的连接程序。我需要线索,但我对他们不够了解。

这可能吗?

正如我所说我是Boost/Asio的新手,我试图遵循一些线程和协程的文档。然后,我把客户端代码在一个方法和服务器在另一:

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

和两个协程:

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(); 
} 

我用一个端口到客户终端和其他端口服务器端点。这是对的吗?需要?

它开始看起来像它会工作。每种方法都同时运行,但是随后在io_service1.run(server_agent方法的最后一行)处发生线程分配错误:

boost :: exception_detail :: clone_impl>在内存位置0x0118C61C处。

有什么建议吗?

+1

至于标题,我敢肯定你可以做一个全双工服务器/客户端客户端/服务器实施与升压asio。只需为进程和客户端连接启用'boost :: acceptor'即可。只是异步处理一切。 –

+0

谢谢。我会尝试这种方法。这似乎是唯一不会陷入循环的方法。 – Neumann

回答

0

您正在描述一个UDP客户端/服务器应用程序。但是你的实现必然会失败。将asio服务器或客户端视为始终在单个线程中运行。

下面的代码只是让你有一个想法。我没有试图编译它。客户端非常相似,但可能需要传输缓冲区,显然取决于应用程序。

这是一个缩短版本,所以你明白了。在最终的应用程序中,您希望添加接收超时等。 TCP服务器的原理与async_listen调用相同。连接的套接字可以存储在shared_ptr中,并被lambdas捕获,几乎会神奇地被破坏。

服务器基本上是一样的,除了没有不断的阅读。如果在同一个进程中同时运行服务器和客户端,则可以依靠run()因服务器而循环,但如果不是,则必须为每个连接调用run()。 run()会在交换结束时退出。

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

谢谢你,MichaëlRoy,你让它看起来很简单,我会尽量使用你的建议。 – Neumann

+0

我从上个月制作的应用程序中复制/粘贴,发现一些剩菜([this,msg])。这比处理select()或epoll/iocp要简单得多。一个完整的可用于openwrt + windows的嵌入式客户端+服务器应用程序在10天内完成。看门狗定时器和错误处理/套接字重置等等非常容易添加到这个模型中,并且不会真正地伤害可读性。性能和CPU使用数量非常好。如果您有外部数据源,则需要添加一个tx队列,但一旦基本框架就位后添加就相对简单。 A ++ for boost.asio。 –