实现一个同步的并发型TCP服务器

1. 相关定义

  1. 同步: 使用会阻塞线程执行的I/O和控制操作,该阻塞会持续到相关操作完成或有错误产生
  2. 并发型: 服务器可以同时处理多个客户端请求

2. 基本流程

  1. 分配一个acceptor套接字并将其绑定到一个特定的TCP端口上.
  2. 服务器执行以下的循环直至停止:
    1. 等待客户端的连接请求
    2. 接受客户端的连接请求(会发生tcp的三次握手)
    3. 产生(spawn)一个控制线程,在这个线程的上下文中:
      • 等待客户端发送的请求信息
      • 读取请求信息内容
      • 处理请求信息内容
      • 将(处理完毕的)请求信息回送给客户端
      • 关闭和客户端的连接并销毁相关套接字

3. 相关源代码

//Compile: g++ -std=c++11 -o server Server.cc -lboost_system -lpthread
#include <boost/asio.hpp>

#include <thread>
#include <atomic>
#include <memory>
#include <iostream>

using namespace boost;

class Service {
 public:
  Service() {}

  void StartHandligClient(std::shared_ptr<asio::ip::tcp::socket> sock) {
    std::thread th(([this, sock]() {
          HandleClient(sock);
    }));

    th.detach();
  }

 private:
  void HandleClient(std::shared_ptr<asio::ip::tcp::socket> sock) {
    try {
      asio::streambuf request;
      asio::read_until(*sock.get(), request, '\n');

      // Emulate request processing.
      int i = 0;
      while (i != 1000000)
        ++i;
      std::this_thread::sleep_for(std::chrono::milliseconds(500));

      // sending response.
      std::string response = "Response\n";
      asio::write(*sock.get(), asio::buffer(response));
    } catch (const system::system_error &e) {
      std::cout << "Error occured! Error code = " << e.code() 
                << ". Message: " << e.what();
    }

    // Clean-up.cause Service instance is allocated in heap;
    delete this;
  }
};

class Acceptor {
 public:
  Acceptor(asio::io_service &ios, unsigned short port_num)
    : ios_(ios), acceptor_(ios_, asio::ip::tcp::endpoint(asio::ip::address_v4::any(), port_num))
  {
    acceptor_.listen();
  }

  void Accept() {
    std::shared_ptr<asio::ip::tcp::socket> sock(new asio::ip::tcp::socket(ios_));

    acceptor_.accept(*sock.get());

    (new Service)->StartHandligClient(sock);
  }

private:
 asio::io_service &ios_;
 asio::ip::tcp::acceptor acceptor_;
};

class Server {
 public:
  Server() : stop_(false)
  {
  }

  void Start(unsigned short port_num) {
    thread_.reset(new std::thread([this, port_num]() {
          Run(port_num);
          }));
  }

  void Stop() {
    stop_.store(true);
    thread_->join();
  }

 private:
  void Run(unsigned short port_num) {
    Acceptor acc(ios_, port_num);

    while (!stop_.load()) {
      acc.Accept();
    }
  }

  std::unique_ptr<std::thread> thread_;
  std::atomic<bool> stop_;
  asio::io_service ios_;
};

int main()
{
  unsigned short port_num = 3333;
  try {
    Server srv;
    srv.Start(port_num);

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

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

4. 缺陷及解决方案

  1. 在Server类中调用Stop()方法后,服务器可能会一直阻塞,直至最后的连接请求到来。(E.g. 当Server的线程阻塞在acc.Accept()时, 调用Stop()方法后,服务器将会等待这些阻塞操作完成而不会停止)

    解决方案:

    • 可以在Stop()方法中创建一个虚拟的主动连接,发送虚拟的请求
    • 创建一个特殊的客户端,发送特殊的关闭服务消息(E.g. stop\n)
  2. 服务器容易被客户端恶意攻击(E.g. 客户端只连接,却不发送请求,那么服务器将一直处于阻塞状态)

    解决方案:

    • 使用非阻塞套接字(将会把我们的服务器转入reactive模式)
    • 使用异步I/O
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值