1

下面是一个我用boost asio写的异步UDP客户端和服务器的例子,用来测试asio性能(速度)。服务器是一个简单的程序,它回显从客户端收到的消息,如下所示:

异步 UDP 服务器:

#include <iostream>
#include <string>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::udp;


class udp_server
{
public:
udp_server(boost::asio::io_service& io_service) : socket_(io_service, udp::endpoint(udp::v4(), 4000))
{
    start_receive();
}

private:
void start_receive()
{
    socket_.async_receive_from(
        boost::asio::buffer(recv_buffer_), remote_endpoint_,
        boost::bind(&udp_server::handle_receive, this,
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));
}

void handle_receive(const boost::system::error_code& error,std::size_t bytes_transferred)
{

    if (!error || error == boost::asio::error::message_size)
    {
        std::string copy(recv_buffer_, bytes_transferred);
        boost::shared_ptr<std::string> message( new std::string(recv_buffer_, bytes_transferred) );

        socket_.async_send_to(boost::asio::buffer(*message), remote_endpoint_,
        boost::bind(&udp_server::handle_send, this, message,
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));

        start_receive();
    }
}

void handle_send(boost::shared_ptr<std::string> /*message*/,
    const boost::system::error_code& /*error*/,
    std::size_t /*bytes_transferred*/)
{
}

udp::socket socket_;
udp::endpoint remote_endpoint_;
char recv_buffer_[300];
};

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

    return 0;
}

客户端只需以异步模式向服务器发送消息并等待回复并从服务器接收回显支持的消息。我使用 chrono 来计算发送和接收消息的经过时间。

客户端代码如下:

#ifdef WIN32
#define _WIN32_WINNT 0x0501
#include <stdio.h>
#endif

#include <fstream>
#include <iostream>
#include <stdlib.h>
#include <chrono>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

using namespace boost::asio;
io_service service;
auto ts = std::chrono::high_resolution_clock::now();

#define MEM_FN(x)       boost::bind(&self_type::x, shared_from_this())
#define MEM_FN1(x,y)    boost::bind(&self_type::x, shared_from_this(),y)
#define MEM_FN2(x,y,z)  boost::bind(&self_type::x, shared_from_this(),y,z)

class talk_to_svr : public boost::enable_shared_from_this<talk_to_svr>, boost::noncopyable {
    typedef talk_to_svr self_type;
    talk_to_svr(const std::string & message, ip::udp::endpoint ep) 
      : sock_(service), started_(true), message_(message), ep_(ep) {}
    void start(ip::udp::endpoint ep) {
        this->t00 = std::chrono::high_resolution_clock::now();
        sock_.async_connect(ep, MEM_FN1(on_connect,_1));
    }
public:
    typedef boost::system::error_code error_code;
    typedef boost::shared_ptr<talk_to_svr> ptr;

    static ptr start(ip::udp::endpoint ep, const std::string & message) {
        ptr new_(new talk_to_svr(message, ep));
        new_->start(ep);
        return new_;
    }
    void stop() {
        if ( !started_) return;
        started_ = false;
        sock_.shutdown(boost::asio::ip::udp::socket::shutdown_both);
        sock_.close();
        service.stop();
        std::cout << "Sucket successfully shutdowned and closed" << std::endl;
    }
    bool started() { return started_; }
private:
    void on_connect(const error_code & err) {
    this->t0 = std::chrono::high_resolution_clock::now();
        if ( !err)      {
            std::cout << "Successfully Connected" << std::endl;
            do_write(message_);

        }
        else            stop();
    }
    void on_read(const error_code & err, size_t bytes) {
        if ( !err) {
        this->t1 = std::chrono::high_resolution_clock::now();
            std::cout << "Client successfully read " << bytes << " bytes of data as "; //<< std::endl;
            std::string copy(read_buffer_, bytes);
            std::cout << copy << std::endl;
            std::cout.precision(6);
            auto Tc = 1.e-9*std::chrono::duration_cast<std::chrono::nanoseconds>(this->t00-ts).count();
            auto Ts = 1.e-9*std::chrono::duration_cast<std::chrono::nanoseconds>(this->t0-ts).count();
            auto Te = 1.e-9*std::chrono::duration_cast<std::chrono::nanoseconds>(this->t1-ts).count();
            std::cout << "Start to connect @time: " << Tc << " seconds" << std::endl;
            std::cout << "Start to send message @time: " << Ts << " second(s)" << std::endl;
            std::cout << "Recived messages @time: " << Te << " second(s)" << std::endl;
            auto dt_du = 1.e-9*std::chrono::duration_cast<std::chrono::nanoseconds>(this->t1-this->t0).count();
            std::cout << "It tooks " << dt_du << " second(s) to send and recieve " << bytes << " bytes of data" << std::endl;
        }
        else {
            std::cout << "Error occured in reading data from server" << std::endl;
        }
    }

    void on_write(const error_code & err, size_t bytes) {
        std::cout << "Client successfully sent " << bytes << " bytes of data" << std::endl;
        do_read();
    }

    void do_read() {

        sock_.async_receive_from(buffer(read_buffer_),
                   ep_,
                   MEM_FN2(on_read,_1,_2));

    }
    void do_write(const std::string & msg) {
        if ( !started() ) return;
        std::copy(msg.begin(), msg.end(), write_buffer_);
        sock_.async_send_to( buffer(write_buffer_, msg.size()), 
                                ep_, MEM_FN2(on_write,_1,_2));
    }

public:
    std::chrono::time_point<std::chrono::high_resolution_clock> t00;
    std::chrono::time_point<std::chrono::high_resolution_clock> t0;
    std::chrono::time_point<std::chrono::high_resolution_clock> t1;

private:
    ip::udp::socket sock_;
    ip::udp::endpoint ep_;
    enum { max_msg = 40 };
    char read_buffer_[max_msg];
    char write_buffer_[max_msg];
    bool started_;
    std::string message_;
};

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

    int port = atoi(argv[2]);
    std::string ipp = std::string(argv[1]);
    std::cout << "Server ip: " << argv[1] << std::endl;
    std::cout << "Server port: " << (port) << std::endl;
    ip::udp::endpoint ep( ip::address::from_string(ipp), port);
    ts = std::chrono::high_resolution_clock::now();
    talk_to_svr::start(ep, "Message");

    service.run();
}

编译和执行服务器:

c++ echo_server.cpp -std=c++11 -o echo_client -I /path/to/boost_1_57_0/ -l pthread -l boost_system -l boost_thread

./echo_server

编译和执行客户端:

c++ echo_client.cpp -std=c++11 -o echo_client -I /path/to/boost_1_57_0/ -l pthread -l boost_system -l boost_thread

./echo_client 127.0.0.1 4000

在同一台机器(本地主机)上运行服务器和客户端时,我想知道为什么 boost asio 的性能如此差,因为它需要 80 微秒(平均)来发送和接收 36 字节(28 字节用于标头 + 8 字节用于数据) 相当于3.4 Mbits/sec !!!!

有什么方法可以提高 UDP 套接字性能或任何其他技巧来优化代码?

4

0 回答 0