前言
非常感谢社长的开源项目TinyWebServer,让我这个自学服务器编程的小白也能感觉受益匪浅
所需前置知识
- va_list 解决变参问题 头文件:<stdarg.h> 重点!在本项目中实现了代码复用,减少代码量
- http响应报文格式
- 基础makefile知识
数据结构
http状态码
//本次仅用到个别状态,可根据需求更改代码
enum HTTP_CODE {
NO_REQUEST = 0,//http解析用到
GET_REQUEST,//http解析用到
BAD_REQUEST,//http解析用到
NO_RESOURCE,
FORBIDDEN_REQUEST,
FILE_REQUEST,
INTERNAL_ERROR,
CLOSED_CONNECTION
};
状态码描述
//响应状态描述
const char *ok_200_title = "OK";
const char *ok_200_form = "<html><body></body></html>";
const char *error_400_title = "Bad Request";
const char *error_400_form = "Your request has bad syntax or is inherently impossible to staisfy.\n";
const char *error_403_title = "Forbidden";
const char *error_403_form = "You do not have permission to get file form this server.\n";
const char *error_404_title = "Not Found";
const char *error_404_form = "The requested file was not found on this server.\n";
const char *error_500_title = "Internal Error";
const char *error_500_form = "There was an unusual problem serving the request file.\n";
项目结构
SourceCode
http_respond.h
#ifndef HTTP_RESPOND_H
#define HTTP_RESPOND_H
#include <stdarg.h>
#include <stdio.h>
class Data {
public:
static const int WRITE_BUFFER_SIZE = 2048;
enum HTTP_CODE {
NO_REQUEST = 0,
GET_REQUEST,
BAD_REQUEST,
NO_RESOURCE,
FORBIDDEN_REQUEST,
FILE_REQUEST,
INTERNAL_ERROR,
CLOSED_CONNECTION
};
public:
Data() {}
~Data() {}
public:
void init(int sockfd,bool is_alive); //初始化数据
bool process_write(HTTP_CODE ret);
private:
bool add_response(const char *format, ...); //可变参写入数据
bool add_status_line(int status, const char *title);
bool add_headers(int content_len);
bool add_content(const char *content);
bool add_content_type();
bool add_content_length(int content_len);
bool add_linger();
bool add_blank_line();
void init();
private:
int m_sockfd;
int m_write_index;
int m_linger;
char m_write_buf[WRITE_BUFFER_SIZE];
};
#endif
http_respond.cpp
#include "./http_respond.h"
#include "string.h"
#include "iostream"
#include "stdarg.h"
//响应状态描述
const char *ok_200_title = "OK";
const char *ok_200_form = "<html><body></body></html>";
const char *error_400_title = "Bad Request";
const char *error_400_form = "Your request has bad syntax or is inherently impossible to staisfy.\n";
const char *error_403_title = "Forbidden";
const char *error_403_form = "You do not have permission to get file form this server.\n";
const char *error_404_title = "Not Found";
const char *error_404_form = "The requested file was not found on this server.\n";
const char *error_500_title = "Internal Error";
const char *error_500_form = "There was an unusual problem serving the request file.\n";
void Data::init(int sockfd,bool is_alive){
m_sockfd=sockfd;
m_linger=is_alive;
init();
}
void Data::init(){
m_write_index=0;
memset(m_write_buf,'\0',WRITE_BUFFER_SIZE);
}
bool Data::process_write(HTTP_CODE ret){
switch(ret){
case INTERNAL_ERROR:{
add_status_line(500,error_500_title);
add_headers(strlen(error_500_form));
if(!add_content(error_500_form))
return false;
break;
}
case BAD_REQUEST:{
add_status_line(404,error_404_title);
add_headers(strlen(error_404_form));
if(!add_content(error_404_form))
return false;
break;
}
case FORBIDDEN_REQUEST:{
add_status_line(403,error_403_title);
add_headers(strlen(error_403_form));
if(!add_content(error_403_form))
return false;
break;
}
case FILE_REQUEST:{
add_status_line(200,ok_200_title);
add_headers(strlen(ok_200_form));
if(!add_content(ok_200_form))
return false;
break;
}
default:
return false;
}
std::cout<<m_sockfd<<"号客户http响应:"<<std::endl;
std::cout<<m_write_buf<<std::endl;
}
// 可变参数写 核心代码
bool Data::add_response(const char *format,...){
if(m_write_index>=WRITE_BUFFER_SIZE)
return false;
va_list arg_list;
va_start(arg_list,format);
int len=vsnprintf(m_write_buf+m_write_index,WRITE_BUFFER_SIZE-1-m_write_index,format,arg_list);
if(len>=WRITE_BUFFER_SIZE-1-m_write_index){
va_end(arg_list);
return false;
}
m_write_index+=len;
va_end(arg_list);
return true;
}
bool Data::add_status_line(int status,const char *title){
return add_response("%s %d %s\r\n","HTTP/1.1",status,title);
}
bool Data::add_headers(int content_len){
add_content_length(content_len);
add_linger();
add_blank_line();
}
bool Data::add_content_length(int content_len){
return add_response("Content-Length:%d\r\n",content_len);
}
bool Data::add_content_type(){
return add_response("Content-Type:%s\r\n","text/html");
}
bool Data::add_linger(){
return add_response("Connection:%s\r\n",(m_linger==true)?"keep-alive":"close");
}
bool Data::add_content(const char *content){
return add_response("%s",content);
}
bool Data::add_blank_line(){
return add_response("%s","\r\n");
}
main.c
#include <iostream>
#include "http/http_respond.h"
int main(){
Data client1;
client1.init(1,true);
client1.process_write(Data::BAD_REQUEST);
Data client2;
client2.init(2,false);
client2.process_write(Data::FILE_REQUEST);
Data client3;
client1.init(3,true);
client1.process_write(Data::INTERNAL_ERROR);
Data client4;
client1.init(4,true);
client1.process_write(Data::FORBIDDEN_REQUEST);
}
makefile
target1 = response
target2= responseDebug
src = $(wildcard ./*.c ./http/*.cpp ./http/*.h)
#release版本 make response
$(target1):$(src)
g++ $^ -o $@
#debug版本 make responseDebug
$(target2):$(src)
g++ $^ -g -o $@
clean:
-rm -f $(target1) $(target2)