非boost下的standalone模式下asio使用

非boost下的standalone模式下asio使用,最近项目需要,学习了下asio库,发现网上给出的基本上都是基于boost的asio或者二次封装后的,对于原生的asio的讲解比较少,最近学习后,总结了一些资料,可供参考,主要是tcp,udp模式下的同步异步读写功能。以下时总结的一些内容,主要是源码(基于vs2017开发的,复制后可以直接编译通过),废话不多说,直接上源码(新手上路,如有疏漏,请见谅)。

  1. asio网络库TCP同步(sync)模式

1.1.服务端(server)

   a.通过tcp::acceptor类创建一个tcp server对象,并绑定端口(也可以不在构造器中自动绑定,而通过bind函数手动绑定);

   b.通过accept函数获取远端连接;

   c.通过远端连接的write_some函数将数据发往客户端

服务端demo

   首先要将asio-1.12.2源码复制到源文件的文件夹中,如果用的是vs2017开发,在属性-VC++中,添加asio的include路径,./asio-1.12.2/include;如果用的是Qt Creator开发,那么在pro文件中添加,INCLUDEPATH += asio-1.12.2/include    DEFINES+=ASIO_STANDALONE,以下demo是vs2017下开发的,源码如下:

#include <iostream>

#include <chrono>

#ifdef _WIN32

#define _WIN32_WINNT 0x0A00

#endif // _WIN32

#define ASIO_STANDALONE

#include <asio.hpp>

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

{

  asio::io_service iosev;

  asio::ip::tcp::acceptor acceptor(iosev, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), 1000));

  for (;;)

  {

      // 创建socket对象

      asio::ip::tcp::socket socket(iosev);

      // 等待,直到用客户端连接进来

      acceptor.accept(socket);

      // 显示连接进来的客户端

      std::cout << socket.remote_endpoint().address() << std::endl;

      // 向客户端发送hello world!

      asio::error_code ec;

      std::string str = "hello world!";

      socket.write_some(asio::buffer(str,str.size()), ec);

      // 如果出错,打印出错信息,ec=0代表写入成功了,其他代表失败

      if (ec)

      {

          std::cout << ec.message() << std::endl;

          break;

      }

      else

      {

          std::cout << "jin ru else" << std::endl;

          //using namespace std::chrono_literals;

          //std::this_thread::sleep_for(2000ms);

          socket.wait(socket.wait_read);

          std::vector<char> vBuffer;

          vBuffer.resize(128);

          asio::error_code error;

          socket.read_some(asio::buffer(vBuffer.data(), vBuffer.size()), error);

          if (error == asio::error::eof)

          {

              break;

          }

          else if (error)

          {

              std::cout << error.message().data() << std::endl;

          }

          else

          {

              std::cout << "123 == " << vBuffer.data() << std::endl;

          }

      }

      // 与当前客户交互完成后循环继续等待下一客户连接

  }

  return 0;

}

    1. 客户端(client)
  1. 通过tcp::socket类定义一个tcp client对象socket;
  2. 通过connect函数连接服务器,打开socket连接;
  3. 通过read_some函数来读数据,另外,还可以通过write_some来写数据,通过close来关闭socket连接(这里是通过释放socket对象隐式释放连接)。

源码demo如下:

#include <iostream>

#ifdef _WIN32

#define _WIN32_WINNT 0x0A00

#endif // _WIN32

#define ASIO_STANDALONE

#include <asio.hpp>

using namespace std;

using asio::ip::tcp;

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

{

    try

    {

        // 通过tcp::socket类定义一个tcp client对象socket

        asio::io_service io;

        tcp::socket socket(io);

        // 通过connect函数连接服务器,打开socket连接

        tcp::endpoint end_point(asio::ip::address::from_string("127.0.0.1"), 1000);

        socket.connect(end_point);

        for (;;)

        {

            std::vector<char> vBuffer;

            vBuffer.resize(128);

            asio::error_code error;

            // 通过read_some函数来读数据

            size_t len = socket.read_some(asio::buffer(vBuffer), error);

            if (error == asio::error::eof)

            {

                break;

            }

            else if (error)

            {

                std::cout << error.message().data() << std::endl;

            }

            else

            {

                // 回复给服务端一个,hello too

                std::string str = "hello too!";

                socket.write_some(asio::buffer(str, str.size()), error);

            }

            cout << vBuffer.data() << endl;

        }

    }

    catch (const std::exception& e)

    {

        cout << e.what() << endl;

    }

    return 0;

}

  1. asio网络库TCP异步(async)模式

2.1.服务端(server)

异步服务器的编写,最核心的一个函数就是acceptor(asio::ip::tcp::acceptor),监听来自客户端的socket连接,如果监听到socket连接,就调用read(socket.async_read_some)读取socket中的数据,接收完毕数据后,可以通过write(asio::async_write)函数来给客户端回复需要的数据。整个异步过程,最核心的就是回调函数,在read,write中可以通过匿名函数,或者传入函数指针的方式进行数据的读写。具体代码如下:

Session.h:服务器的读写操作都在这里

AsyncServer.h:acceptor的实现

asyncServerMain.cpp:测试代码

依赖关系:AsyncServeMain.cpp依赖AsyncServer.h,AsyncServer.h依赖Session.h.

asyncServerMain.cpp的源码

#include <iostream>

#include "AsyncServer.h"

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

{

  try

  {

      asio::io_context io_context;

      AsyncServer s(io_context, 1000);

      io_context.run();

  }

  catch (const std::exception& e)

  {

      std::cout << e.what() << std::endl;

  }

  return 0;

}

AsyncServer.h的源码:

#pragma once

#include "Session.h"

class AsyncServer

{

public:

  AsyncServer(asio::io_context& io_context, short port):acceptor_(io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(),port))

  {

      do_accept();

  }

private:

  void do_accept()

  {

      acceptor_.async_accept(

          [this](std::error_code ec, asio::ip::tcp::socket socket)

      {

          if (!ec)

          {

              std::make_shared<Session>(std::move(socket))->start();

              std::cout << "accept success" << std::endl;

          }

          do_accept();

      });

  }

private:

  asio::ip::tcp::acceptor acceptor_;

};

Session.h的源码:

#pragma once

#include <cstdlib>

#include <iostream>

#include <memory>

#define ASIO_STANDALONE

#include <asio.hpp>

using asio::ip::tcp;

class Session : public std::enable_shared_from_this<Session>

{

public:

  Session(tcp::socket socket) : socket_(std::move(socket))

  {

      std::cout << "Session " << std::endl;

  }

  void start()

  {

      do_read();

  }

  ~Session()

  {

      std::cout << "~Session " << std::endl;

  }

private:

  void do_read()

  {

      auto self(shared_from_this());

      // 异步监听,当有信息发送过来,就调用lamda

      socket_.async_read_some(asio::buffer(data_, max_length),

          [this, self](std::error_code ec, std::size_t length)

      {

          if (!ec)

          {

              std::cout << "self.use_count():" << self.use_count() << std::endl;

              std::cout << data_ << std::endl;

              do_write(length);

          }

          else

          {

              std::cout << "async_read_some else" << std::endl;

          }

      });

  }

  void do_write(std::size_t length)

  {

      auto self(shared_from_this());

      // 发送指定字节的数据

      asio::async_write(socket_, asio::buffer(data_, length),

          [this, self](std::error_code ec,std::size_t length)

          {

              if (!ec)

              {

                  do_read();

              }

          });

  }

private:

  tcp::socket socket_;

  enum {max_length = 1024};

  char data_[max_length];

 

};

2.2.客户端(client)

        客户端主要通过connect函数来连接服务端,支持域名解析,同时采用心跳保活模式,定时发送一条数据给服务端,这里只展示了如何发送心跳数据,业务数据发送流程和这个一样,如果想要保证数据的顺序性,可以使用一个queue来存放要发送的数据,这样就可以避免多次调用write函数而导致的数据乱序问题。具体参考客户端代码:

client.h:包含客户端的连接,读写等操作

asyncClient.cpp:测试函数

依赖关系:asyncClient.cpp依赖client.h

    client.h的源码:

#pragma once

#include <functional>

#include <iostream>

#include <string>

#ifdef _WIN32

#define _WIN32_WINNT 0x0A00

#endif // _WIN32

#define ASIO_STANDALONE

#include <asio.hpp>

#include <asio/buffer.hpp>

#include <asio/io_context.hpp>

#include <asio/ip/tcp.hpp>

#include <asio/read_until.hpp>

#include <asio/steady_timer.hpp>

#include <asio/write.hpp>

using asio::steady_timer;

using asio::ip::tcp;

using std::placeholders::_1;

using std::placeholders::_2;

class client

{

public:

  client(asio::io_context& io_context) : socket_(io_context),

      deadline_(io_context),

      heartbeat_timer_(io_context)

  {

  }

  void start(tcp::resolver::results_type endpoints)

  {

      endpoints_ = endpoints;

      start_connect(endpoints_.begin());

      deadline_.async_wait(std::bind(&client::check_deadline, this));

  }

  void stop()

  {

      stopped_ = true;

      std::error_code ignored_error;

      socket_.close(ignored_error);

      deadline_.cancel();

      heartbeat_timer_.cancel();

  }

private:

  void start_connect(tcp::resolver::results_type::iterator endpoint_iter)

  {

      if (endpoint_iter != endpoints_.end())

      {

          std::cout << "Trying " << endpoint_iter->endpoint() << "...\n" << std::endl;

          deadline_.expires_after(std::chrono::seconds(60));

          socket_.async_connect(endpoint_iter->endpoint(),

              std::bind(&client::handle_connect,this,_1,endpoint_iter));

      }

      else

      {

          stop();

      }

  }

  void handle_connect(const std::error_code& error, tcp::resolver::results_type::iterator endpoint_iter)

  {

      if (stopped_)

      {

          return;

      }

      if (!socket_.is_open())

      {

          std::cout << "Connect timed out\n";

          start_connect(++endpoint_iter);

      }

      else if (error)

      {

          std::cout << "Connect error:" << error.message() << "\n";

          socket_.close();

          start_connect(++endpoint_iter);

      }

      else

      {

          std::cout << "Connect to " << endpoint_iter->endpoint() << "\n";

          start_read();

          start_write();

      }

  }

  void start_read()

  {

      deadline_.expires_after(std::chrono::seconds(30));

      asio::async_read_until(socket_, asio::dynamic_buffer(input_buffer_), '\n',

          std::bind(&client::handle_read, this, _1, _2));

  }

  void handle_read(const std::error_code& error,std::size_t n)

  {

      if (stopped_)

      {

          return;

      }

      if (!error)

      {

          std::string line(input_buffer_.substr(0, n - 1));

          input_buffer_.erase(0, n);

          if (!line.empty())

          {

              std::cout << "Received: " << line << "\n";

          }

          start_read();

      }

      else

      {

          std::cout << "Error on receive:" << error.message() << "\n";

          stop();

      }

  }

  void start_write()

  {

      if (stopped_)

      {

          return;

      }

      asio::async_write(socket_, asio::buffer("hello async server\n", 20), std::bind(&client::handle_write, this, _1));

  }

  void handle_write(const std::error_code& error)

  {

      if (stopped_)

      {

          return;

      }

      if (!error)

      {

          heartbeat_timer_.expires_after(std::chrono::seconds(10));

          heartbeat_timer_.async_wait(std::bind(&client::start_write, this));

      }

      else

      {

          std::cout << "Error on heartbeat:" << error.message() << "\n";

          stop();

      }

  }

  void check_deadline()

  {

      if (stopped_)

      {

          return;

      }

      if (deadline_.expiry() <= steady_timer::clock_type::now())

      {

          socket_.close();

          deadline_.expires_at(steady_timer::time_point::max());

      }

      deadline_.async_wait(std::bind(&client::check_deadline,this));

  }

private:

  bool stopped_ = false;

  tcp::resolver::results_type endpoints_;

  tcp::socket socket_;

  std::string input_buffer_;

  steady_timer deadline_;

  steady_timer heartbeat_timer_;

};

    asyncClient.cpp的源码:

#include <iostream>

#include "client.h"

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

{

  try

  {

      asio::io_context io_context;

      asio::ip::tcp::resolver r(io_context);

      client c(io_context);

      c.start(r.resolve("127.0.0.1", "1000"));

      io_context.run();

  }

  catch (const std::exception& e)

  {

      std::cerr << "Exception: " << e.what() << "\n";

  }

  return 0;

}

  1. asio网络库UDP同步(sync)模式

3.1.服务端(server)

   Udp的服务的很简单,只需要开启一个接收socket,通过receive_from函数读取接收到客户端发送到指定断开的数据即可。

源码如下:

#include <iostream>

#include <cstdlib>

#ifdef _WIN32

#define _WIN32_WINNT 0x0A00

#endif // _WIN32

#define ASIO_STANDALONE

#include <asio.hpp>

using asio::ip::udp;

enum {max_length = 1024};

void server(asio::io_context& io_context, unsigned short port)

{

  udp::socket sock(io_context, udp::endpoint(udp::v4(), port));

  for (;;)

  {

      char data[max_length];

      udp::endpoint sender_endpoint;

      size_t length = sock.receive_from(asio::buffer(data, max_length), sender_endpoint);

      sock.send_to(asio::buffer(data, length), sender_endpoint);

      if (length > 0)

      {

          std::cout.write(data, length);

          std::cout << "\n";

      }

  }

}

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

{

  try

  {

      asio::io_context io_context;

      server(io_context, 32100);

  }

  catch (const std::exception& e)

  {

      std::cerr << "Exception: " << e.what() << "\n";

  }

  return 0;

}

3.2.客户端(client)

源码如下:

#include <iostream>

#include <cstring>

#include <cstdlib>

#ifdef _WIN32

#define _WIN32_WINNT 0x0A00

#endif // _WIN32

#define ASIO_STANDALONE

#include <asio.hpp>

using asio::ip::udp;

enum {max_length = 1024};

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

{

  try

  {

      asio::io_context io_context;

      udp::socket s(io_context, udp::endpoint(udp::v4(), 0));

      udp::resolver resolver(io_context);

      udp::resolver::results_type endpoints = resolver.resolve(udp::v4(), "127.0.0.1", "32100");

      std::cout  << endpoints.begin()->endpoint() << "\n";

      std::cout << "Enter message:";

      char request[max_length];

      std::cin.getline(request, max_length);

      size_t request_length = std::strlen(request);

      s.send_to(asio::buffer(request, request_length), *endpoints.begin());

      char reply[max_length];

      udp::endpoint sender_endpoint;

      size_t reply_length = s.receive_from(asio::buffer(reply, max_length), sender_endpoint);

      std::cout << "Reply port is:" << sender_endpoint << " Reply is: ";

      std::cout.write(reply, reply_length);

      std::cout << "\n";

  }

  catch (const std::exception& e)

  {

      std::cerr << "Exception: " << e.what() << "\n";

  }

  return 0;

}

  1. asio网络库UDP异步(async)模式

4.1.服务端(server)

udp的服务端异步操作和同步几乎一样,只不过调用的时带有async_前缀的函数,往指定端口发送数据即可,代码如下:

#include <iostream>

#include <cstdlib>

#ifdef _WIN32

#define _WIN32_WINNT 0x0A00

#endif // _WIN32

#define ASIO_STANDALONE

#include <asio.hpp>

using asio::ip::udp;

class server

{

public:

  server(asio::io_context& io_context, short port)

      : socket_(io_context, udp::endpoint(udp::v4(), port))

  {

      do_receive();

  }

  void do_receive()

  {

      socket_.async_receive_from(asio::buffer(data_,max_length),sender_endpoint_,

          [this](std::error_code ec, std::size_t bytes_recvd)

      {

          if (!ec && bytes_recvd > 0)

          {

              do_send(bytes_recvd);

          }

          else

          {

              do_receive();

          }

      });

  }

  void do_send(std::size_t length)

  {

      socket_.async_send_to(asio::buffer(data_,length),sender_endpoint_,

          [this](std::error_code ec, std::size_t bytes_send)

      {

          do_receive();

      });

  }

private:

  udp::socket socket_;

  udp::endpoint sender_endpoint_;

  enum {max_length = 1024};

  char data_[max_length];

};

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

{

  try

  {

      asio::io_context io_context;

      server(io_context,6668);

      io_context.run();

  }

  catch (const std::exception& e)

  {

      std::cerr << "Exception: " << e.what() << "\n";

  }

  return 0;

}

4.2.客户端(client)

客户端代码如下:

asio_timer.h源码如下(事件循环,服务端循环写数据到指定端口):

#pragma once

#ifndef ASIO_TIMER_ASIO_TIMER_H_

#define ASIO_TIMER_ASIO_TIMER_H_

#include <chrono>

#include <functional>

#define ASIO_STANDALONE

#include "asio.hpp"

#include "asio/steady_timer.hpp"

namespace asiotimer {

  template <typename TimeUnit>

  class TimerManager {

  public:

      struct TimerItem {

          TimerItem(asio::io_service & io_service, int seconds,

              const std::function<void()> & f) : timer(io_service),

              duration(seconds),

              func(f) {}

          asio::steady_timer timer;

          TimeUnit duration;

          std::function<void()> func;

      };

      TimerManager(asio::io_service & io_service) : io_service_(io_service) {}

      TimerManager(TimerManager &) = delete;

      TimerManager & operator = (const TimerManager &) = delete;

      template <typename T>

      void Add(T * obj, const unsigned int & duration, void (T::* mem_func)()) {

          std::function<void()> func = std::bind(mem_func, obj);

          this->items_.push_back(std::make_shared<TimerItem>(

              this->io_service_,

              duration,

              func));

      }

      void Run() {

          for (auto & item : this->items_) {

              asio::steady_timer & timer = item->timer;

              const TimeUnit & duration = item->duration;

              const std::function<void()> & func = item->func;

              TimerLoop(timer, duration, func);

          }

      }

  protected:

      void TimerLoop(asio::steady_timer & timer,

          const TimeUnit & duration,

          const std::function<void()> & func) {

          timer.expires_from_now(duration);

          timer.async_wait(

              [this, &timer, duration, func](const asio::error_code &) {

              func();

              TimerLoop(timer, duration, func);

          });

      }

  private:

      asio::io_service & io_service_;

      std::vector< std::shared_ptr<TimerItem> > items_;

  };

}    // namespace asiotimer

#endif    // ASIO_TIMER_ASIO_TIMER_H_

asyncClient.cpp的源码如下:

#include <iostream>

#include <cstdlib>

#include <functional>

#include <memory>

#include <thread>

#ifdef _WIN32

#define _WIN32_WINNT 0x0A00

#endif // _WIN32

#define ASIO_STANDALONE

#include <asio.hpp>

#include <asio/buffer.hpp>

#include <asio/io_context.hpp>

#include <asio/ip/udp.hpp>

#include "asio_timer.h"

using asio::ip::udp;

using std::placeholders::_1;

using std::placeholders::_2;

using namespace std;

asio::ip::udp::endpoint ep(asio::ip::address::from_string("127.0.0.1"), 6668);

class noncopyable {

protected:

    constexpr noncopyable() = default;

    ~noncopyable() = default;

    noncopyable(const noncopyable &) = delete;

    noncopyable &operator= (const noncopyable &) = delete;

};

class client : public enable_shared_from_this<client>,noncopyable

{

public:

    client(asio::io_service& io_context, const std::string & message)

        : sock_(io_context, asio::ip::udp::endpoint(asio::ip::udp::v4(), 6668)), started_(true), message_(message),

        timer_manager_(io_context)

    {

        timer_manager_.Add(this, 1, &client::Timer1Sec);

        timer_manager_.Run();

        start();

    }

    typedef shared_ptr<client> ptr;

    void start() {

        do_write(message_);

    }

    bool started() { return started_; }

private:

    void on_read(const error_code & err, size_t bytes) {

        if (!err) {

            std::string copy(read_buffer_, bytes);

            std::cout << "server echoed us " << copy << std::endl;

            //<< (copy == message_ ? "OK" : "FAIL") << std::endl;

        }

        start();

    }

    void on_write(const error_code & err, size_t bytes) {

        //printf("client write result:%d, bytes:%d \n", err.value(), bytes);

        do_read();

    }

    void do_read() {

        sock_.async_receive_from(asio::buffer(read_buffer_), sender_ep,

            bind(&client::on_read,

                this,

                std::placeholders::_1,

                std::placeholders::_2));

        std::cout << sender_ep << std::endl;

    }

    void do_write(const std::string & msg) {

        std::copy(msg.begin(), msg.end(), write_buffer_);

        sock_.async_send_to(asio::buffer(write_buffer_, msg.size()), ep,

            bind(&client::on_write,

                this,

                std::placeholders::_1,

                std::placeholders::_2));

    }

    void Timer1Sec() {

        start();

        std::cout << "Timer1Sec." << std::endl;

    }

private:

    asiotimer::TimerManager<std::chrono::seconds> timer_manager_;

private:

    asio::ip::udp::socket sock_;

    asio::ip::udp::endpoint sender_ep;

    enum { max_msg = 1024 };

    char read_buffer_[max_msg];

    char write_buffer_[max_msg];

    bool started_;

    std::string message_;

};

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

{

    try

    {

        asio::io_service io_service;

        client c(io_service, "hello world");

        io_service.run();

        system("pause");

    }

    catch (const std::exception& e)

    {

        std::cerr << "Exception: " << e.what() << "\n";

    }

    return 0;

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值