服务器和客户端同时使用Boost-Asio

时间:2017-06-22 19:28:21

标签: c++ multithreading sockets boost

我是一名57岁的AspNet程序员。因为我是唯一一个在C ++开始工作的人,我的老板要求我为需要具有非常特定特征的沟通代理的客户提供服务。它可以作为多个平台上的守护程序运行,有时也可以作为客户端和服务器。我不太了解,但我必须解决问题并在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&gt;在内存位置0x0118C61C。

有什么建议吗?

1 个答案:

答案 0 :(得分: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;
}