为了更好的理解http协议,笔者使用了C++/socket模拟了一个http服务器, 其中的服务器使用了epoll的方式,并针对每一个新的连接开启新线程处理
大致分为三个部分,具体代码可见
1. socket 接入部分
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <iostream>
#include <thread>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAX_CLIENT 20
void httpProc(int fd);
int main(int argc, char **argv)
{
int hostfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
epoll_event ev, evClients[MAX_CLIENT];
int epollfd = epoll_create(1024);
ev.data.fd = hostfd;
ev.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, hostfd, &ev);
sockaddr_in sin;
sin.sin_family = AF_INET;
inet_pton(AF_INET, "0.0.0.0", &sin.sin_addr.s_addr);
sin.sin_port = htons(8989);
bind(hostfd, (const sockaddr *)&sin, sizeof(sin));
listen(hostfd, 20);
std::cout << "server start listen port:" << sin.sin_port << std::endl;
int eps = 0;
socklen_t length = 0;
while(true) {
eps = epoll_wait(epollfd, evClients, MAX_CLIENT, 100);
for(int i = 0; i < eps; ++i) {
if(evClients[i].data.fd == hostfd) {
int nfd = accept(hostfd, (sockaddr *)&sin, &length);
if(nfd < 0) {
std::cerr << " nfd < 0" << std::endl;
return -1;
}
char ip[16];
std::cout << "accept from: " << inet_ntop(AF_INET, &sin.sin_addr.s_addr, ip, sizeof(sin)) << ":" << sin.sin_port << std::endl;
ev.data.fd = nfd;
ev.events = EPOLLIN | EPOLLET;
fcntl(nfd, F_SETFL, fcntl(nfd, F_GETFL) | O_NONBLOCK);
epoll_ctl(epollfd, EPOLL_CTL_ADD, nfd, &ev);
} else if(evClients[i].events & EPOLLIN) {
int fd = evClients[i].data.fd;
if(fd < 0) continue;
std::thread worker([fd]() {
httpProc(fd);
});
worker.detach();
}
}
}
return 0;
}
2. 请求解析部分
class HttpParser
{
public:
void setSocket(int socket);
void parse();
std::string method() const;
std::vector<std::string> headerNames() const;
std::string header(const std::string& headerName);
std::string path() const;
std::string protocolVersion() const;
std::string body() const;
protected:
int readByte(char &c);
std::string readLine();
std::string readn(int max);
private:
int _fd;
std::string _method;
std::string _path;
std::string _protocolVersion;
std::map<std::string, std::string> _headers;
std::string _body;
int r = 0, w = 0;
char buffer[1024];
};
void HttpParser::setSocket(int socket)
{
_fd = socket;
}
void HttpParser::parse()
{
std::string line = readLine();
std::istringstream iss(line);
std::cout << "正在读取响应状态\n";
iss >> _method >> _path >> _protocolVersion;
std::cout << "正在读取报头信息\n";
line = readLine();
while(!line.empty()) {
auto colonIdx = line.find_first_of(':');
auto name = line.substr(0, colonIdx);
auto value = line.substr(colonIdx + 1);
_headers[name] = value;
line = readLine();
}
std::cout << "正在读取http正文长度\n";
int contentLength = 0;
auto Content_Length = _headers["Content-Length"];
if(!Content_Length.empty()) {
contentLength = atoi(Content_Length.c_str());
}
std::cout << "正文长度:" << contentLength << std::endl;
std::cout << "正在读取http正文\n";
_body.assign(readn(contentLength));
}
std::string HttpParser::header(const std::string &headerName)
{
return _headers[headerName];
}
std::string HttpParser::path() const
{
return _path;
}
std::string HttpParser::method() const
{
return _method;
}
std::string HttpParser::protocolVersion() const
{
return _protocolVersion;
}
std::vector<std::string> HttpParser::headerNames() const
{
std::vector<std::string> names;
for(auto& pair: _headers) {
names.push_back(pair.first);
}
return names;
}
std::string HttpParser::body() const
{
return _body;
}
int HttpParser::readByte(char &c)
{
if(r == w) {
r = 0;
if((w = recv(_fd, buffer, 1024, 0)) == 0)
return 0;
}
c = buffer[r++];
return 1;
}
std::string HttpParser::readLine()
{
char c = '\0';
std::string result;
result.reserve(1024);
while(true) {
if(!readByte(c)) break;
result.push_back(c);
if(c == '\n') break;
}
return result.substr(0, result.find_first_of("\r\n"));
}
std::string HttpParser::readn(int max)
{
char c = '\0';
std::string result;
result.reserve(max + 1);
for(int i = 0; i < max; ++i) {
if(!readByte(c)) break;
result.push_back(c);
}
return result;
}
3. 模拟响应部分
void httpProc(int fd)
{
HttpParser req;
req.setSocket(fd);
req.parse();
std::cout << "method: " << req.method() << std::endl;
for(auto& headerName: req.headerNames()) {
std::cout << headerName << ": " << req.header(headerName) << std::endl;
}
std::cout << "body:\n" << req.body() << std::endl;
auto body = "{\"status\": \"OK\"}";
auto fmt =
"HTTP/1.1 200 OK \r\n"
"Server: nginx\r\n"
"Accept-Encoding:gzip, deflate\r\n"
"Accept-Language:zh-CN,zh;q=0.9\r\n"
"Cache-Control:no-cache\r\n"
"Connection:keep-alive\r\n"
"Content-Type:application/json;charset=UTF-8\r\n"
"Content-Length: %d\r\n"
"\r\n"
"%s";
char res[1024];
sprintf(res, fmt, strlen(body), body);
auto size = strlen(res);
send(fd, res, size , 0);
close(fd);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)