记录一下最近这学期学习的sylar服务器框架项目,输出整理一下项目的结构,用到的知识和自己的体会
项目仓库地址
https://github.com/sylar-yin/sylar/
整理博客过程中参考的大佬资料链接:
============================================
基础介绍
该模块实现了一个HTTP服务器,继承自TcpServer并重载了handleClient方法,将accept后的socket封装成HttpSession,便于接收与发送http消息
HttpSession
HttpSession类继承自SocketStream类,封装了服务器里的http会话,提供了发送响应和接收请求的接口
// Http会话类,继承自SocketStream
class HttpSession : public SocketStream {
public:
typedef std::shared_ptr
<HttpSession> ptr;
HttpSession(Socket::ptr sock, bool owner = true);
HttpRequest::ptr recvRequset(); // 接收请求报文
int sendResponse(HttpResponse::ptr rsp); // 发送响应报文
private:
};
HttpSession::HttpSession(Socket::ptr sock, bool owner)
:SocketStream(sock, owner) {
}
HttpRequest::ptr HttpSession::recvRequset() {
// 创建解析器
HttpRequestParser::ptr parser(new HttpRequestParser);
// 获取缓冲区大小
uint64_t buff_size = HttpRequestParser::GetHttpRequestBufferSize();
// 将缓冲区交给智能指针托管
std::shared_ptr
<char> buffer(
new char[buff_size], [](char* ptr){
delete[] ptr;
});
char* data = buffer.get();
int offset = 0;
// 解析工作循环
do {
int len = read(data + offset, buff_size - offset);
if(len <= 0) {
return nullptr;
}
len += offset;
size_t nparse = parser->execute(data, len);
if(parser->hasError()) {
return nullptr;
}
offset = len - nparse;
if(offset == (int)buff_size) {
return nullptr;
}
if(parser->isFinished()) {
break;
}
} while(true);
// 获取body长度
int64_t length = parser->getContentLength();
if(length > 0) {
std::string body;
body.resize(length);
int len = 0;
if(length >= offset) {
memcpy(&body[0], data, length);
len = offset;
} else {
memcpy(&body[0], data, length);
len = length;
}
length -= offset;
if(length > 0) {
if(readFixSize(&body[len], length) <= 0) {
return nullptr;
}
}
// 设置body
parser->getData()->setBody(body);
}
std::string keep_alive = parser->getData()->getHeader("Connection");
if(!strcmp(keep_alive.c_str(), "keep-alive")) {
parser->getData()->setClose(false);
}
// 返回解析完成的request
return parser->getData();
}
int HttpSession::sendResponse(HttpResponse::ptr rsp) {
std::stringstream ss;
ss << *rsp;
std::string data = ss.str();
return writeFixSize(data.c_str(), data.size());
}
HttpServer
// HTTP服务器类(继承自TCPServer)
class HttpServer : public TcpServer {
public:
typedef std::shared_ptr
<HttpServer> ptr;
HttpServer(bool keepalive = false
, sylar::IOManager* worker = sylar::IOManager::GetThis()
, sylar::IOManager* accept_worker = sylar::IOManager::GetThis());
ServletDispatch::ptr getServletDispatch() const { return m_dispatch; }
void setServletDispatch(ServletDispatch::ptr v) { m_dispatch = v; }
protected:
virtual void handleClient(Socket::ptr client) override;
private:
bool m_isKeepalive; // 是否长连接
ServletDispatch::ptr m_dispatch; // Servlet
};
HttpServer::HttpServer(bool keepalive, sylar::IOManager* worker, sylar::IOManager* accept_worker)
: TcpServer(worker, accept_worker)
, m_isKeepalive(keepalive)
, m_dispatch(new ServletDispatch) {
}
void HttpServer::handleClient(Socket::ptr client) {
HttpSession::ptr session(new HttpSession(client));
// 工作循环
do {
// 接收请求报文
auto req = session->recvRequset();
if(!req) {
SYLAR_LOG_WARN(g_logger) << "recv http request fail, errno="
<< errno << " errstr=" << strerror(errno)
<< " client:" << *client;
break;
}
// 创建响应报文
HttpResponse::ptr rsp(new HttpResponse(req->getVersion(), req->isClose() || !m_isKeepalive));
// 设置Header信息
rsp->setHeader("Server", getName());
// 执行路由函数
m_dispatch->handle(req, rsp, session);
// 发送响应报文
session->sendResponse(rsp);
// 若不支持长连接或请求关闭,退出
if(!m_isKeepalive || req->isClose()) {
break;
}
} while(true);
// 关闭会话
session->close();
}
测试
服务器在localhost的8020端口上监听,添加了/hello路径下的servlet路由,会返回欢迎信息
#include "sylar/http/http_server.h"
#include "sylar/log.h"
sylar::Logger::ptr g_logger = SYLAR_LOG_ROOT();
void run() {
g_logger->setLevel(sylar::LogLevel::INFO);
sylar::Address::ptr addr = sylar::Address::LookupAnyIPAddress("0.0.0.0:8020");
if(!addr) {
SYLAR_LOG_ERROR(g_logger) << "get address error";
return;
}
sylar::http::HttpServer::ptr http_server(new sylar::http::HttpServer(true));
while(!http_server->bind(addr)) {
SYLAR_LOG_ERROR(g_logger) << "bind" << *addr << "fail";
sleep(1);
}
auto sd = http_server->getServletDispatch();
sd->addServlet("/hello", [](sylar::http::HttpRequest::ptr req
, sylar::http::HttpResponse::ptr rsp
, sylar::http::HttpSession::ptr session) {
rsp->setBody("Welcome to sylar/1.0.0!");
return 0;
});
http_server->start();
}
int main(int argc, char** argv) {
sylar::IOManager iom(2);
iom.schedule(run);
return 0;
}
开启服务器

使用浏览器访问,可以看到出现了欢迎信息,服务器也打印了相关日志


总结
封装了一个HttpServer,重载了基类的handleClient方法并声明为protected,意味着还可以继续派生




严肃品鉴中……