c ++ - Boost ASIO网络服务器/客户端

时间:2017-01-04 23:14:54

标签: c++ boost network-programming

我为客户端和服务器编写了一个程序。该计划目前执行以下操作:

  1. 服务器侦听连接的终点
  2. 客户端连接到服务器
  3. 服务器在接受连接时发送消息
  4. 客户端收到消息
  5. 我正在异步这样做。但是,问题是他们只能发送/接收一条消息。之后,他们就会终止。以下是我的代码:

    #include <iostream>
    #include <vector>
    #include<boost/asio.hpp>
    
    std::vector<char> buff(256);
    
    void SendHandler(boost::system::error_code ex){
        std::cout << " do something here" << std::endl;
    }
    
    void ReadHandler(boost::system::error_code ex){
        std::cout << " print the buffer data..." << std::endl;
        std::cout << buff.data() << std::endl;
    
    }
    
    void Server(){
        boost::asio::io_service service;
        using namespace boost::asio::ip;
        tcp::endpoint endpoint(tcp::v4(), 4000);
        tcp::acceptor acceptor(service, endpoint); 
        tcp::socket socket(service);
        std::cout << "[Server] Waiting for connection" << std::endl;
    
    
        acceptor.accept(socket);
        std::cout << "[Server] Accepted a connection from client" << std::endl;
    
        std::string msg = "Message from server";
        socket.async_send(boost::asio::buffer(msg), SendHandler);
        service.run();
    
    }
    
    void Client(){
        boost::asio::io_service service;
        using namespace boost::asio::ip;
        tcp::endpoint endpoint(address::from_string("127.0.0.1"), 4000);
        tcp::socket socket(service);
        std::cout << "[Client] Connecting to server..." << std::endl;
        socket.connect(endpoint);
        std::cout << "[Client] Connection successful" << std::endl;
    
        socket.async_read_some(boost::asio::buffer(buff), ReadHandler);
        service.run();
    }
    
    int main(int argc, char **argv) {
        if(argc == 1){
            std::cout << "Please specify s for server or c for client" << std::endl;
            return -1;
        }
        if(argv[1][0] == 's'){
            Server();
        }
        else{
            Client();
        }
        return 0;
    }
    

    我想缩放此程序,以便:

    1. 服务器可以监听,客户端可以无限期地发送请求。更像是一种单向聊天系统。
    2. 服务器可以连接到多个客户端。
    3. async_send()service.run()置于无限循环中并没有帮助。它只是在客户端反复打印消息,直到客户端终止。

      我对boost::asio甚至network programming都很新。请告诉我在代码中应该修改的位置和内容?

2 个答案:

答案 0 :(得分:4)

从学习一些基础知识开始。库提供了很好的教程和示例,可以引导您完成同步和异步服务器的概念和示例。

http://www.boost.org/doc/libs/1_62_0/doc/html/boost_asio.html

从头开始。使用计时器和回调来完成基本概念。继续学习网络教程。通过编辑教程程序,在几个小时的时间内构建核心思想,自己尝试创意。

然后继续前往示例部分。 &#39;聊天&#39;示例非常接近您要构建的内容。此特定示例显示如何通过在读取处理程序中重新发出async_reads来打开连接。只要服务线程继续使用指定的async_accept任务运行,异步asio服务器将为多个客户端提供服务。

http://www.boost.org/doc/libs/1_62_0/doc/html/boost_asio/examples/cpp11_examples.html http://www.boost.org/doc/libs/1_62_0/doc/html/boost_asio/example/cpp11/chat/chat_server.cpp

答案 1 :(得分:0)

实现异步 TCP 服务器

异步 ​​TCP 服务器是满足以下条件的分布式应用程序的一部分:

  • 在客户端-服务器通信模型中充当服务器
  • 通过 TCP 协议与客户端应用程序通信
  • 使用异步 I/O 和控制操作
  • 可以同时处理多个客户

典型的异步 TCP 服务器根据以下算法工作:

  • 分配一个接受者套接字并将其绑定到特定的 TCP 端口。
  • 启动异步接受操作。
  • 生成一个或多个控制线程并将它们添加到运行 Boost.Asio 事件循环的线程池中。
  • 当异步接受操作完成后,启动一个新的接受下一个连接请求。
  • 启动异步读取操作以读取来自连接客户端的请求。
  • 异步读取操作完成后,处理请求并准备响应消息。
  • 发起异步写入操作,向客户端发送响应消息。
  • 异步写入操作完成后,关闭连接并释放套接字。
//responsible for handling a single client by reading the request message, processing it, and then sending back the response message.
//Each instance of the Service class is intended to handle one connected client
//by reading the request message, processing it, and then sending the response message back.
class Service
{
public:
    //The class's constructor accepts a shared pointer to an object representing a socket connected to a particular client as an argument
    // and caches this pointer. This socket will be used later to communicate with the client application.
    Service(std::shared_ptr<asio::ip::tcp::socket> sock) : m_sock(sock)

    //This method starts handling the client by initiating the asynchronous reading operation
    //to read the request message from the client specifying the onRequestReceived() method as a callback.
    void StartHandling()

private:
    void onRequestReceived(const boost::system::error_code &ec,
                           std::size_t bytes_transferred)


    void onResponseSent(const boost::system::error_code &ec,
                        std::size_t bytes_transferred)

    // Here we perform the cleanup.
    void onFinish()
    {
        delete this;
    }

    //To keep things simple,  we implement a dummy service which only emulates the execution of certain operations
    //The request processing emulation consists of performing many increment operations to emulate operations
    //that intensively consume CPU and then putting the thread of control to sleep for some time to emulate I/O operations
    std::string ProcessRequest(asio::streambuf &request)

private:
    std::shared_ptr<asio::ip::tcp::socket> m_sock;
    std::string m_response;
    asio::streambuf m_request;
};

//responsible for accepting the connection requests arriving from clients and instantiating the objects of the Service class,
// which will provide the service to connected clients.
class Acceptor
{
public:
    //Its constructor accepts a port number on which it will listen for the incoming connection requests as its input argument. 
    Acceptor(asio::io_service &ios, unsigned short port_num) : m_ios(ios),
                                                               //The object of this class contains an instance of the asio::ip::tcp::acceptor class as its member named m_acceptor,
                                                               //which is constructed in the Acceptor class's constructor.
                                                               m_acceptor(m_ios,
                                                                          asio::ip::tcp::endpoint(
                                                                              asio::ip::address_v4::any(),
                                                                              port_num)),
                                                               m_isStopped(false)

    //The Start() method is intended to instruct an object of the Acceptor class to start listening and accepting incoming connection requests.
    void Start()

    // Stop accepting incoming connection requests.
    void Stop()


private:
    void InitAccept()

    void onAccept(const boost::system::error_code &ec,
                  std::shared_ptr<asio::ip::tcp::socket> sock)

private:
    asio::io_service &m_ios;
    //used to asynchronously accept the incoming connection requests.
    asio::ip::tcp::acceptor m_acceptor;
    std::atomic<bool> m_isStopped;
};

//represents the server itself
class Server
{
public:
    Server()

    // Start the server.
    // Accepts a protocol port number on which the server should listen for the incoming connection requests
    // and the number of threads to add to the pool as input arguments and starts the server
    // Nonblocking Method
    void Start(unsigned short port_num,
               unsigned int thread_pool_size)

    // Stop the server.
    // Blocks the caller thread until the server is stopped and all the threads running the event loop exit.
    void Stop()

private:
    asio::io_service m_ios;
    std::unique_ptr<asio::io_service::work> m_work;
    std::unique_ptr<Acceptor> acc;
    std::vector<std::unique_ptr<std::thread>> m_thread_pool;
};

int main()
{
    unsigned short port_num = 3333;

    try
    {
        //it instantiates an object of the Server class named srv.
        Server srv;

        //before starting the server, the optimal size of the pool is calculated.
        // The general formula often used in parallel applications to find the optimal number of threads is the number of processors the computer has multiplied by 2.
        // We use the std::thread::hardware_concurrency() static method to obtain the number of processors. 
        unsigned int thread_pool_size =
            std::thread::hardware_concurrency() * 2;

        //because this method may fail to do its job returning 0,
        // we fall back to default value represented by the constant DEFAULT_THREAD_POOL_SIZE, which is equal to 2 in our case.
        if (thread_pool_size == 0)
            thread_pool_size = DEFAULT_THREAD_POOL_SIZE;

        srv.Start(port_num, thread_pool_size);

        std::this_thread::sleep_for(std::chrono::seconds(60));

        srv.Stop();
    }
    catch (system::system_error &e)
    {
        std::cout << "Error occured! Error code = "
                  << e.code() << ". Message: "
                  << e.what();
    }

    return 0;
}

实现异步 TCP 客户端

支持异步执行请求和请求取消功能的异步 TCP 客户端应用程序:

  • 来自用户的输入应该在一个单独的线程中处理——用户界面线程。此线程不应被阻塞很长时间。
  • 用户应该能够向不同的服务器发出多个请求。
  • 用户应该能够在之前发出的请求完成之前发出新的请求。
  • 用户应该能够在之前发出的请求完成之前取消它们。
// Function pointer type that points to the callback
// function which is called when a request is complete.
// Based on the values of the parameters passed to it, it outputs information about the finished request.
typedef void (*Callback)(unsigned int request_id,        // unique identifier of the request is assigned to the request when it was initiated.
                         const std::string &response,    // the response data
                         const system::error_code &ec);  // error information

// data structure whose purpose is to keep the data related to a particular request while it is being executed
struct Session
{
    Session(asio::io_service &ios,
            const std::string &raw_ip_address,
            unsigned short port_num,
            const std::string &request,
            unsigned int id,
            Callback callback) : m_sock(ios),
                                 m_ep(asio::ip::address::from_string(raw_ip_address),
                                      port_num),
                                 m_request(request),
                                 m_id(id),
                                 m_callback(callback),
                                 m_was_cancelled(false) {}

    asio::ip::tcp::socket m_sock; // Socket used for communication
    asio::ip::tcp::endpoint m_ep; // Remote endpoint.
    std::string m_request;        // Request string.

    // streambuf where the response will be stored.
    asio::streambuf m_response_buf;
    std::string m_response; // Response represented as a string.

    // Contains the description of an error if one occurs during
    // the request lifecycle.
    system::error_code m_ec;

    unsigned int m_id; // Unique ID assigned to the request.

    // Pointer to the function to be called when the request
    // completes.
    Callback m_callback;

    bool m_was_cancelled;
    std::mutex m_cancel_guard;
};

// class that provides the asynchronous communication functionality.
class AsyncTCPClient : public boost::noncopyable
{
public:
    AsyncTCPClient(unsigned char num_of_threads)

    // initiates a request to the server
    void emulateLongComputationOp(
        unsigned int duration_sec,          //represents the request parameter according to the application layer protocol
        const std::string &raw_ip_address,  //specify the server to which the request should be sent.
        unsigned short port_num,            //specify the server to which the request should be sent.
        Callback callback,                  //callback function, which will be called when the request is complete.
        unsigned int request_id)    // unique identifier of the request


    // cancels the previously initiated request designated by the request_id argument
    void cancelRequest(unsigned int request_id) //accepts an identifier of the request to be canceled as an argument.


    // blocks the calling thread until all the currently running requests complete and deinitializes the client.
    void close()


private:
    // method is called whenever the request completes with any result.
    void onRequestComplete(std::shared_ptr<Session> session)

private:
    asio::io_service m_ios;
    std::map<int, std::shared_ptr<Session>> m_active_sessions;
    std::mutex m_active_sessions_guard;
    std::unique_ptr<boost::asio::io_service::work> m_work;
    std::list<std::unique_ptr<std::thread>> m_threads;
};

// a function that will serve as a callback, which we'll pass to the AsyncTCPClient::emulateLongComputationOp() method
// It outputs the result of the request execution and the response message to the standard output stream if the request is completed successfully
void handler(unsigned int request_id,
             const std::string &response,
             const system::error_code &ec)

int main()
{
    try
    {
        AsyncTCPClient client(4);

        // Here we emulate the user's behavior.

        // creates an instance of the AsyncTCPClient class and then calls its emulateLongComputationOp() method to initiate three asynchronous requests
        // User initiates a request with id 1.
        client.emulateLongComputationOp(10, "127.0.0.1", 3333, handler, 1);

        // Decides to exit the application.
        client.close();
    }
    catch (system::system_error &e)
    {
        std::cout << "Error occured! Error code = " << e.code()
                  << ". Message: " << e.what();

        return e.code().value();
    }

    return 0;
};

设置环境

1.安装 CMake

cd ~
wget https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5.tar.gz
tar xf cmake-3.14.5.tar.gz
cd cmake-3.14.5
./bootstrap --parallel=10
make -j4
sudo make -j4 install

2.安装 Boost

cd ~
wget https://boostorg.jfrog.io/artifactory/main/release/1.69.0/source/boost_1_69_0.tar.gz
tar xf boost_1_69_0.tar.gz
cd boost_1_69_0
./bootstrap.sh
./b2 ... cxxflags="-std=c++0x -stdlib=libc++" linkflags="-stdlib=libc++" ...
sudo ./b2 toolset=gcc -j4 install

如何构建

mkdir build
cd build
cmake ..
cmake --build .

如何运行

运行服务器

./bin/server

我们可以检查服务器是否运行

netstat -tulpn | grep LISTEN

tcp        0      0 0.0.0.0:445             0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:3333            0.0.0.0:*               LISTEN      6866/./bin/server   <===============
tcp        0      0 0.0.0.0:139             0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:5433          0.0.0.0:*               LISTEN      -
tcp6       0      0 :::445                  :::*                    LISTEN      -
tcp6       0      0 :::5000                 :::*                    LISTEN      -
tcp6       0      0 :::5001                 :::*                    LISTEN      -
tcp6       0      0 :::139                  :::*                    LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -
tcp6       0      0 ::1:631                 :::*                    LISTEN      -

运行客户端

./bin/client

Request #1 has completed. Response: Response from server

您可以在 Boost Asio C++ Network Programming Client Server

中找到该项目