【问题标题】:How do I create a Boost.Asio Server that can handle multiple clients at once?如何创建一个可以同时处理多个客户端的 Boost.Asio 服务器?
【发布时间】:2015-10-13 06:52:34
【问题描述】:

我可以创建一个可以响应一个客户端的简单 TCP 服务器,但是我不知道如何创建一个可以同时处理多个客户端的服务器。我提到了 TCP 日间异步服务器等示例,但它只是将数据发送到客户端。只要客户端存在,我需要创建的是保持连接。客户端和服务器都将使用 Json 进行通信。考虑一种情况,客户端将提供{"hello":"Client"},而服务器应响应{"Hello":"Server"},并说另一种{"message":"How are you?"} 及其响应{"response" : "Fine"}。我需要一次处理多个客户。我阅读了聊天服务器文档,但它太难理解了。有人可以给出如何使用 Boost.Asio 启动代码的基本流程吗?谢谢。

下面是给出的代码:

#include <ctime>
#include <iostream>
#include <string>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
#include <json/reader.h>
#include <json/writer.h>

Json::Value GetRootFromJson(std::string json)
{
    Json::Value root;
    Json::Reader reader;
    bool success = reader.parse(json, root);
    return root;
}

std::string GetJsonFromRoot(Json::Value root)
{
    Json::FastWriter writer;
    std::string json = writer.write(root);
    return json;

}

using boost::asio::ip::tcp;

std::string make_daytime_string()
{
  using namespace std; // For time_t, time and ctime;
  time_t now = time(0);
  return ctime(&now);
}

class tcp_connection
  : public boost::enable_shared_from_this<tcp_connection>
{
public:
  typedef boost::shared_ptr<tcp_connection> pointer;

  static pointer create(boost::asio::io_service& io_service)
  {
    return pointer(new tcp_connection(io_service));
  }

  tcp::socket& socket()
  {
    return socket_;
  }

  void start()
  {
    //message_ = make_daytime_string();

    //Read from client, make json and send appropriate response
    boost::asio::async_read(socket_, boost::asio::buffer(message_),
    boost::bind(&tcp_connection::handle_read, shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));
    std::string messageP(message_);
    std::cout << messageP << std::endl;
    Json::Value root = GetRootFromJson(messageP);
    std::string isHello = root["hello"].asString();
    std::string isMessage = root["message"].asString();
    if(!isHello.empty())
    {
        std::string messageTemp = "{\"Hello\":\"Server\"}";
        boost::asio::async_write(socket_, boost::asio::buffer(messageTemp),
        boost::bind(&tcp_connection::handle_write, shared_from_this(),
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
    }

    if(!isMessage.empty())
    {
        std::string messageTemp = "{\"response\":\"Fine\"}";
        boost::asio::async_write(socket_, boost::asio::buffer(messageTemp),
        boost::bind(&tcp_connection::handle_write, shared_from_this(),
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
    }



    /*
    boost::asio::async_write(socket_, boost::asio::buffer(message_),
        boost::bind(&tcp_connection::handle_write, shared_from_this(),
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
    */
  }

private:
  tcp_connection(boost::asio::io_service& io_service)
    : socket_(io_service)
  {
  }

  void handle_write(const boost::system::error_code& /*error*/,
      size_t /*bytes_transferred*/)
  {
  }
  void handle_read(const boost::system::error_code& /*error*/,
      size_t /*bytes_transferred*/)
  {
      std::cout << "Handle Read of connection\n";
  }

  tcp::socket socket_;
  std::string message_;
};

class tcp_server
{
public:
  tcp_server(boost::asio::io_service& io_service)
    : acceptor_(io_service, tcp::endpoint(tcp::v4(), 1936))
  {
    start_accept();
  }

private:
  void start_accept()
  {
    tcp_connection::pointer new_connection =
      tcp_connection::create(acceptor_.get_io_service());

    acceptor_.async_accept(new_connection->socket(),
        boost::bind(&tcp_server::handle_accept, this, new_connection,
          boost::asio::placeholders::error));
  }

  void handle_accept(tcp_connection::pointer new_connection,
      const boost::system::error_code& error)
  {
    if (!error)
    {
        std::cout << "A client connected" << std::endl;
      new_connection->start();
    }

    start_accept();
  }

  tcp::acceptor acceptor_;
};

int main()
{
  try
  {
    boost::asio::io_service io_service;
    tcp_server server(io_service);
    io_service.run();
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}

运行它给了我:

错误 2 错误 C2679:二进制“=”:未找到采用“const boost::asio::const_buffer”类型的右侧操作数的运算符(或没有可接受的转换)C:\boost_1_55_0_dyn \boost\asio\detailsumption_buffers.hpp 175

编辑:我添加了一个非常简单的客户端代码,它将"{\"Hello\":\"Client\"}" 发送到服务器并期望输出。但是 sehe 给出的服务器代码无法进入 handle_read

#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

int main(int argc, char* argv[])
{
  try
  {

    boost::asio::io_service io_service;

    tcp::resolver resolver(io_service);
    tcp::resolver::query query("localhost", "1936");
    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);

    tcp::socket socket(io_service);
    boost::asio::connect(socket, endpoint_iterator);

    std::string message = "{\"hello\":\"Client\"}";
    std::cout << "Sending the message: " << message << std::endl;
    socket.send(boost::asio::buffer(message));
    std::cout << "Sent the message: " << message << std::endl;
    boost::asio::streambuf buf;
    size_t len;
    std::string messageReceived;
    boost::asio::streambuf::mutable_buffers_type mbuf = buf.prepare(512);
    std::cout << "Now receiving message\n";
    std::string messageServer;
    try
    {
        boost::asio::streambuf buf;
        size_t len;
        boost::asio::streambuf::mutable_buffers_type mbuf = buf.prepare(512);
        do {
            len = socket.receive(mbuf);
            std::cout << len << std::endl;
            std::string str(boost::asio::buffers_begin(mbuf), boost::asio::buffers_begin(mbuf) + len);
            messageServer = messageServer + str;
        } while (len>=512); 
    }
    catch (boost::system::system_error err)
    {
        std::cout << err.code() << " " << err.what();
    }

    std::cout << messageServer << std::endl;
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}

这是实现服务器的正确方法吗?谢谢。

【问题讨论】:

  • 你的代码在哪里?甚至示例也实现了您描述的所有内容。你特别坚持什么? (我们不是代码编写服务)
  • 等一下,我会附上我想要的东西。谢谢。
  • 谢赫,我附上了代码。
  • 不用告诉我。你没有为我添加它。 (如果需要,我可以收藏这个问题)
  • 构成问题一部分的代码必须包含在问题中,而不是从其他地方链接。

标签: c++ sockets boost tcp boost-asio


【解决方案1】:

据记载,

boost::asio::async_read(socket_, boost::asio::buffer(message_),

获取一个流缓冲区。

此外,它是一个异步操作,因此在发布读取操作之后、回调完成处理程序 (handle_read) 之前访问 message_意义。 p>

以下至少为我编译:

#include <ctime>
#include <iostream>
#include <string>

#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>

#include <json/reader.h>
#include <json/writer.h>

namespace {
    Json::Value to_json(std::string json)
    {
        Json::Value root;
        Json::Reader reader;
        /*bool success =*/ reader.parse(json, root);
        return root;
    }

    std::string to_string(Json::Value root) // unused TODO FIXME
    {
        Json::FastWriter writer;
        std::string json = writer.write(root);
        return json;
    }
}

using boost::asio::ip::tcp;

class tcp_connection : public boost::enable_shared_from_this<tcp_connection>
{
  public:
    typedef boost::shared_ptr<tcp_connection> pointer;

    static pointer create(boost::asio::io_service& io_service)
    {
        return pointer(new tcp_connection(io_service));
    }

    tcp::socket& socket()
    {
        return socket_;
    }

    void start()
    {
        //Read from client, make json and send appropriate response
        boost::asio::async_read(socket_, message_,
                boost::bind(&tcp_connection::handle_read, shared_from_this(),
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));
    }

  private:
    tcp_connection(boost::asio::io_service& io_service)
        : socket_(io_service)
    {
    }

    void handle_write(const boost::system::error_code& /*error*/,
            size_t /*bytes_transferred*/)
    {
    }

    void handle_read(const boost::system::error_code& error, size_t bytes_transferred)
    {
        std::cout << "Handle Read of connection\n";

        if (error && error != boost::asio::error::eof) {
            std::cout << "Error: " << error.message() << "\n";
            return;
        }

        std::string messageP;
        {
            std::stringstream ss;
            ss << &message_;
            ss.flush();
            messageP = ss.str();
        }

        std::cout << messageP << std::endl;

        Json::Value root      = to_json(messageP);
        std::string isHello   = root["hello"].asString();
        std::string isMessage = root["message"].asString();
        if(!isHello.empty())
        {
            std::string messageTemp = "{\"Hello\":\"Server\"}";
            boost::asio::async_write(socket_, boost::asio::buffer(messageTemp),
                    boost::bind(&tcp_connection::handle_write, shared_from_this(),
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred));
        }

        if(!isMessage.empty())
        {
            std::string messageTemp = "{\"response\":\"Fine\"}";
            boost::asio::async_write(socket_, boost::asio::buffer(messageTemp),
                    boost::bind(&tcp_connection::handle_write, shared_from_this(),
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred));
        }
    }

    tcp::socket socket_;
    boost::asio::streambuf message_;
};

class tcp_server
{
  public:
    tcp_server(boost::asio::io_service& io_service)
        : acceptor_(io_service, tcp::endpoint(tcp::v4(), 1936))
    {
        start_accept();
    }

  private:
    void start_accept()
    {
        tcp_connection::pointer new_connection =
            tcp_connection::create(acceptor_.get_io_service());

      acceptor_.async_accept(new_connection->socket(),
          boost::bind(&tcp_server::handle_accept, this, new_connection,
            boost::asio::placeholders::error));
    }

    void handle_accept(tcp_connection::pointer new_connection, const boost::system::error_code& error)
    {
        if (!error)
        {
            std::cout << "A client connected" << std::endl;
            new_connection->start();
        }

        start_accept();
    }

    tcp::acceptor acceptor_;
};

int main()
{
    try
    {
        boost::asio::io_service io_service;
        tcp_server server(io_service);
        io_service.run();
    }
    catch (std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
}

【讨论】:

  • 我已经编译了你的代码。虽然编译成功,但我创建的简单客户端无法获得响应。 start() 函数被调用,但代码没有进入 handle_read,因此客户端被触发到函数 socket.receive(...)。这是怎么回事?
  • 在Visual Studio下start()添加断点并运行客户端时,message_显示数据,但handle_read 没有被调用。
  • 嗯。我想知道 /where/ 你把断点放在哪里。当然会出现数据。直到发送者关闭连接!这实际上是我修复中的一个问题:错误处理现在将 EOF 视为错误。您可能希望以不同方式处理 eof(更新答案)
  • 断点放置在 tcp_connectionstart() 处。这是截图link。你能说出这里出了什么问题吗?
  • 就像我说的,你怎么知道客户端何时关闭连接?
【解决方案2】:

您需要的内容。有些可能是不必要的:

boost/asio.hppboost/thread.hppboost/asio/io_service.hpp

boost/asio/spawn.hppboost/asio/write.hppboost/asio/buffer.hpp

boost/asio/ip/tcp.hppiostreamstdlib.harraystring

vectorstring.hstdio.hprocess.hiterator

using namespace boost::asio;
using namespace boost::asio::ip;

io_service ioservice;

tcp::endpoint sim_endpoint{ tcp::v4(), 4066 };              //{which connectiontype, portnumber}
tcp::acceptor sim_acceptor{ ioservice, sim_endpoint };
std::vector<tcp::socket> sim_sockets;

static int iErgebnis;
int iSocket = 0;


void do_write(int a)                                        //gets the postion of the socket in the vector
{
    int iWSchleife = 1;                                     //to stay connected with putty or something
    static char chData[32000];
    std::string sBuf = "Received!\r\n";

    while (iWSchleife > 0)          
    {
        boost::system::error_code error;
        memset(chData, 0, sizeof(chData));

        iErgebnis = sim_sockets[a].read_some(boost::asio::buffer(chData), error);           //recv wie bei winsock. simulator empfängt
        iWSchleife = iErgebnis;                                                             //if iErgebnis is bigger then 0 it will stay in the loop. iErgebniss is always >0 when data is received

        if (iErgebnis > 0) {
            printf("%d Zeichen empf.vom Client : \n%s\n\n", iErgebnis, chData);
            write(sim_sockets[a], boost::asio::buffer(sBuf), error);
        }
        else {
            boost::system::error_code ec;
            sim_sockets[a].shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec);       //close the socket when no data
            if (ec)
            {
                printf("studown error");                                                    // An error occurred.
            }
        }
    }
}

void do_accept(yield_context yield)
{
    while (1)                                                   //endless loop to accept limitless clients
    {
        sim_sockets.emplace_back(ioservice);                    //look to the link below for more info
        sim_acceptor.async_accept(sim_sockets.back(), yield);   //waits here to accept an client

        boost::thread dosome(do_write, iSocket);                //when accepts starts the thread do_write and passes the parameter iSocket
        iSocket++;                                              //to know the position of the socket in the vector

    }
}

int main()
{
    sim_acceptor.listen();
    spawn(ioservice, do_accept);            //here you can learn more about Coroutines https://theboostcpplibraries.com/boost.coroutine
    ioservice.run();                        //from here you jump to do:accept
    getchar(); 
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-24
    • 2021-02-03
    • 2011-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-25
    相关资源
    最近更新 更多