1. 相关定义
- 同步: 使用会阻塞线程执行的I/O和控制操作,该阻塞会持续到相关操作完成或有错误产生
- 并发型: 服务器可以同时处理多个客户端请求
2. 基本流程
- 分配一个acceptor套接字并将其绑定到一个特定的TCP端口上.
- 服务器执行以下的循环直至停止:
- 等待客户端的连接请求
- 接受客户端的连接请求(会发生tcp的三次握手)
- 产生(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. 缺陷及解决方案
在Server类中调用Stop()方法后,服务器可能会一直阻塞,直至最后的连接请求到来。(E.g. 当Server的线程阻塞在acc.Accept()时, 调用Stop()方法后,服务器将会等待这些阻塞操作完成而不会停止)
解决方案:
- 可以在Stop()方法中创建一个虚拟的主动连接,发送虚拟的请求
- 创建一个特殊的客户端,发送特殊的关闭服务消息(E.g.
stop\n
)
服务器容易被客户端恶意攻击(E.g. 客户端只连接,却不发送请求,那么服务器将一直处于阻塞状态)
解决方案:
- 使用非阻塞套接字(将会把我们的服务器转入reactive模式)
- 使用异步I/O