asio编写echo服务器(C++asio网络库相关)

echo服务器:服务器接收客户端消息再原封不动回传给客户端,可以保证服务器完整收到客户端消息包再回传完整信息
比如写gameserver的时候首先就需要一个echo知道两端通信是没问题的,还可以改造成一个时间戳的包
特点:
收到客户端消息长度是不确定的

以下是BOOST库中的例子:

#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>

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

class session : public std::enable_shared_from_this<session> {
public:
  session(tcp::socket socket) : socket_(std::move(socket)) {
		//start();
	}

  void start() { do_read(); }

private:
  void do_read() {
    auto self(shared_from_this());
    socket_.async_read_some(
        boost::asio::buffer(data_, max_length),
        [this, self](boost::system::error_code ec, std::size_t length) {
          if (!ec) {
            do_write(length);
          }
        });
  }

  void do_write(std::size_t length) {
    auto self(shared_from_this());
    boost::asio::async_write(
        socket_, boost::asio::buffer(data_, length),
        [this, self](boost::system::error_code ec, std::size_t /*length*/) {
          if (!ec) {
            do_read();
          }
        });
  }

  tcp::socket socket_;
  enum { max_length = 1024 };
  char data_[max_length];
};

class server {
public:
  server(boost::asio::io_service &io_service, short port)
      : acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
        socket_(io_service) {
   // do_accept();
  }
	void start() {
		do_accept();
	}

private:
  void do_accept() {
    acceptor_.async_accept(socket_, [this](boost::system::error_code ec) {
      if (!ec) {
        auto newSession = std::make_shared<session>(std::move(socket_));
        newSession->start();
      }

      do_accept();
    });
  }

  tcp::acceptor acceptor_;
  tcp::socket socket_;
};

int main(int argc, char *argv[]) {
  try {
    if (argc != 2) {
      std::cerr << "Usage: async_tcp_echo_server <port>\n";
      return 1;
    }

    boost::asio::io_service io_service;

    server s(io_service, std::atoi(argv[1]));
		s.start();

    io_service.run();
  } catch (std::exception &e) {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

通过io_service.run()驱动异步事件

tcp::v4() = 0.0.0.0,程序本身不要做客户端连接限制,最好由运维在linux系统下进行设置

server类下通过自定义start()方法可以控制服务器启动时机

newSession表示对客户端socket的构造出了作用域会析构,session的内部调用了shared_from_this()引用计数会加1,所以newsession析构没有关系

asio中的socket支持move,move本身资源相当于恢复初始化,还保存了数据结构
使用move应该有构造函数的良好行为:
在这里插入图片描述
[this, self]的目的是为了让智能指针不析构,其中的self是自身类的拷贝,生命周期和lambda函数一样长,引用计数又会加1,此时引用计数为3
当self析构时减1,出了newsession作用域减1此时资源并未被析构,保存到lambda函数中去了

同理在do_write中auto self(shared_from_this());首先让自己的引用计数加1,因为在do_read()中处理完do_write()函数自身会被析构
就这样这个类的智能指针反复在do_read()和do_write()之间传递以防止被析构直到出错调用析构,出错就意味着客户端退出

在构造函数中不能使用enable_shared_from_this,因为自身类的构造都还没有完成,在析构函数中也不能使用enable_shared_from_this

发布了142 篇原创文章 · 获赞 34 · 访问量 2008

猜你喜欢

转载自blog.csdn.net/qq_39885372/article/details/104070255