南昌seo推广外包,营销型网站建设优化建站,wordpress站关注别人,重庆网站设计制作案例在当前的网络编程专栏前十几篇文章里#xff0c;我已经说明了TCPIP常用的一些原理#xff0c;那么接下来我将逐步进入到实战编程阶段#xff1a;本篇文章我将带大家用C做一个http服务器。既然想实现一个http服务器#xff0c;首先必须要熟悉的就是http协议知识#xff0c;… 在当前的网络编程专栏前十几篇文章里我已经说明了TCPIP常用的一些原理那么接下来我将逐步进入到实战编程阶段本篇文章我将带大家用C做一个http服务器。既然想实现一个http服务器首先必须要熟悉的就是http协议知识然后在选择具体的模块来完成实现。下面先了解一些http协议知识然后我们再一步一步来实现它。http服务器http协议知识一、 网络通信简介传输层及其以下的机制由内核提供应用层由用户进程提供应用程序对通讯数据的含义进行解释而传输层及其以下处理通讯的细节将数据从一台计算机通过一定的路径发送到另一台计算机。应用层数据通过协议栈发到网络上时每层协议都要加上一个数据首部header称为封装Encapsulation。假设现在应用层协议为http那么其中的Data 可以看作是一个http请求或者应答Data包含真正的消息正文和app首部即报头等。 二、HTTP协议详解之请求篇http请求由三部分组成分别是请求行、消息报头、请求正文1、请求行以一个方法符号开头以空格分开后面跟着请求的URI和协议的版本格式如下Method Request-URI HTTP-Version CRLF 其中 Method表示请求方法Request-URI是一个统一资源标识符HTTP-Version表示请求的HTTP协议版本CRLF表示回车和换行除了作为结尾的CRLF外不允许出现单独的CR或LF字符。请求方法所有方法全为大写有多种各个方法的解释如下GET 请求获取Request-URI所标识的资源 POST 在Request-URI所标识的资源后附加新的数据 HEAD 请求获取由Request-URI所标识的资源的响应消息报头 PUT 请求服务器存储一个资源并用Request-URI作为其标识 DELETE 请求服务器删除Request-URI所标识的资源 TRACE 请求服务器回送收到的请求信息主要用于测试或诊断 CONNECT 保留将来使用 OPTIONS 请求查询服务器的性能或者查询与资源相关的选项和需求2、请求报头后述 3、请求正文(略)三、HTTP协议详解之响应篇在接收和解释请求消息后服务器返回一个HTTP响应消息。HTTP响应也是由三个部分组成分别是状态行、消息报头、响应正文 1、状态行格式如下HTTP-Version Status-Code Reason-Phrase CRLF 其中HTTP-Version表示服务器HTTP协议的版本Status-Code表示服务器发回的响应状态代码Reason-Phrase表示状态代码的文本描述。状态代码由三位数字组成第一个数字定义了响应的类别且有五种可能取值1xx指示信息--表示请求已接收继续处理 2xx成功--表示请求已被成功接收、理解、接受 3xx重定向--要完成请求必须进行更进一步的操作 4xx客户端错误--请求有语法错误或请求无法实现 5xx服务器端错误--服务器未能实现合法的请求2、响应报头后述3、响应正文就是服务器返回的资源的内容四、HTTP协议详解之消息报头篇HTTP消息由客户端到服务器的请求和服务器到客户端的响应组成。请求消息和响应消息都是由开始行对于请求消息开始行就是请求行对于响应消息开始行就是状态行消息报头可选空行只有CRLF的行消息正文可选组成。HTTP消息报头包括普通报头、请求报头、响应报头、实体报头。每一个报头域都是由名字“”空格值 组成消息报头域的名字是大小写无关的。1、普通报头 在普通报头中有少数报头域用于所有的请求和响应消息但并不用于被传输的实体只用于传输的消息。Cache-Control 用于指定缓存指令缓存指令是单向的响应中出现的缓存指令在请求中未必会出现且是独立的一个消息的缓存指令不会影响另一个消息处理的缓存机制HTTP1.0使用的类似的报头域为Pragma。请求时的缓存指令包括no-cache用于指示请求或响应消息不能缓存、no-store、max-age、max-stale、min-fresh、only-if-cached; 响应时的缓存指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age、s-maxage.Date普通报头域表示消息产生的日期和时间Connection普通报头域允许发送指定连接的选项。2、请求报头 请求报头允许客户端向服务器端传递请求的附加信息以及客户端自身的信息。常用的请求报头 Accept Accept-Charset Accept-Encoding Accept-Language Authorization Host发送请求时该报头域是必需的 User-Agent3、响应报头 响应报头允许服务器传递不能放在状态行中的附加响应信息以及关于服务器的信息和对Request-URI所标识的资源进行下一步访问的信息。常用的响应报头 Location Server WWW-Authenticate4、实体报头 请求和响应消息都可以传送一个实体。一个实体由实体报头域和实体正文组成但并不是说实体报头域和实体正文要在一起发送可以只发送实体报头域。实体报头定义了关于实体正文eg有无实体正文和请求所标识的资源的元信息。常用的实体报头 Content-Encoding Content-Language Content-Length Content-Type Last-Modified Expires GMT五、HTTP协议相关技术补充1、基础高层协议有文件传输协议FTP、电子邮件传输协议SMTP、域名系统服务DNS、网络新闻传输协议NNTP和HTTP协议等 中介由三种代理(Proxy)、网关(Gateway)和通道(Tunnel)一个代理根据URI的绝对格式来接受请求重写全部或部分消息通过 URI的标识把已格式化过的请求发送到服务器。网关是一个接收代理作为一些其它服务器的上层并且如果必须的话可以把请求翻译给下层的服务器协议。一 个通道作为不改变消息的两个连接之间的中继点。当通讯需要通过一个中介(例如防火墙等)或者是中介不能识别消息的内容时通道经常被使用。代理(Proxy)一个中间程序它可以充当一个服务器也可以充当一个客户机为其它客户机建立请求。请求是通过可能的翻译在内部或经过传递到其它的 服务器中。一个代理在发送请求信息之前必须解释并且如果可能重写它。代理经常作为通过防火墙的客户机端的门户代理还可以作为一个帮助应用来通过协议处 理没有被用户代理完成的请求。网关(Gateway)一个作为其它服务器中间媒介的服务器。与代理不同的是网关接受请求就好象对被请求的资源来说它就是源服务器发出请求的客户机并没有意识到它在同网关打交道。网关经常作为通过防火墙的服务器端的门户网关还可以作为一个协议翻译器以便存取那些存储在非HTTP系统中的资源。通道(Tunnel)是作为两个连接中继的中介程序。一旦激活通道便被认为不属于HTTP通讯尽管通道可能是被一个HTTP请求初始化的。当被中继 的连接两端关闭时通道便消失。当一个门户(Portal)必须存在或中介(Intermediary)不能解释中继的通讯时通道被经常使用。2、协议分析的优势—HTTP分析器检测网络攻击 以模块化的方式对高层协议进行分析处理将是未来入侵检测的方向。HTTP及其代理的常用端口80、3128和8080在network部分用port标签进行了规定3、HTTP协议Content Lenth限制漏洞导致拒绝服务攻击 使用POST方法时可以设置ContentLenth来定义需要传送的数据长度例如ContentLenth:999999999在传送完成前内 存不会释放攻击者可以利用这个缺陷连续向WEB服务器发送垃圾数据直至WEB服务器内存耗尽。这种攻击方法基本不会留下痕迹。4、利用HTTP协议的特性进行拒绝服务攻击的一些构思 服务器端忙于处理攻击者伪造的TCP连接请求而无暇理睬客户的正常请求毕竟客户端的正常请求比率非常之小此时从正常客户的角度看来服务器失去响应这种情况我们称作服务器端受到了SYNFlood攻击SYN洪水攻击。而Smurf、TearDrop等是利用ICMP报文来Flood和IP碎片攻击的。本文用“正常连接”的方法来产生拒绝服务攻击。19端口在早期已经有人用来做Chargen攻击了即Chargen_Denial_of_Service但是他们用的方法是在两台Chargen 服务器之间产生UDP连接让服务器处理过多信息而DOWN掉那么干掉一台WEB服务器的条件就必须有2个1.有Chargen服务2.有HTTP 服务 方法攻击者伪造源IP给N台Chargen发送连接请求ConnectChargen接收到连接后就会返回每秒72字节的字符流实际上根据网络实际情况这个速度更快给服务器。如何实现一个Web服务器1.本服务器采用基于线程池和epoll实现的Reactor模式的简单HTTP服务器。主进程只管理监听socket连接socket都由进程池中的worker进行管理。当有新的连接到来时主进程会通过socketpair创建的套接字和worker进程通信通知子进程接收新连接。子进程正确接收连接之后会把该套接字上的读写事件注册到自己的epll内核事件表中。之后该套接字上的任何I/O操作都由被选中的worker来处理直到客户关闭连接或超时。epoll相关可查阅二十深入浅出TCPIP之epoll的一些思考2.每个子进程都是一个reactor采用epoll和非阻塞I/O实现事件循环。如下图a. epoll负责监听事件的发生有事件到来将调用相应的事件处理单元进行处理1). 信号信号是一种异步事件信号处理函数和程序的主循环是两条不同的执行路线很显然信号处理函数需要尽可能的执行完成以确保信号不被屏蔽信号是不会排队的。一个典型的解决方案是把信号的主要处理逻辑放到事件循环里当信号处理函数被触发时只是通过管道将信号通知给主循环接收和处理信号只需要将和信号处理函数通信的管道的可读事件添加到epoll里。这样信号就能和其他I/O事件一样被处理。2). 定时器事件。使用timefd同样通过监听timefd上的可读事件来统一事件源。将其设置为边沿触发不然timefd水平触发将一直告知该事件。a).忽略SIGPIPE信号当读写一个对端关闭的连接时将为SIGINTSIGTERMSIGCHILD对父进程来说标识有子进程状态发生变化一般是子进程结束设置信号处理函数。a). 超时将通过连接池回收连接。1). 通过非阻塞I/o和事件循环来将阻塞进程的方法分解。例如每次recv新数据时如果recv返回EAGAIN错误都不会一直循环recv而是将现有数据先处理然后记录当前连接状态然后将读事件接着放到epoll队列中监听等待下一个数据到来。因为每次都不会尽可能的将I/O上的数据读取所以我采用了水平触发而不是边沿触发。send同理。i. 对一个连接来说主要监听的就是读就绪事件和写就绪事件。ii. 统一事件源b. 连接池和线程池的实现i. 连接池连接池采用一个数组实现。连接池在构造时传入最大连接数初始化为最大连接数个连接且后续数目不可变。新连接到来时调用http_conn::init当客户端有网络数据发送的时候将当前连接传入到线程池里通过线程池来处理逻辑。ii. 线程池线程池的实现是通过连接类来完成的。连接类在第一次被初始化时即第一次被使用将申请相应的定时器接收和发送缓存。之后将不会将申请的内存销毁直到进程结束。通过这样来降低申请和释放内存的次数来减少内存碎片以及节约时间。c. 连接每个连接都应该有一个void http_conn::init( int sockfd, const sockaddr_in addr )函数此函数会在第一次被调用时分配内存另外还有void http_conn::process() 这个函数将根据操作类型来决定要进行的是读process_read还是写操作process_write。同时根据操作结果返回相应的状态来决定要给epoll添加什么事件。 f. Http报文请求行和头部解析i. 通过状态机m_check_state 来实现HTTP报文的解析。因为一个请求有可能不是在一个tcp包中到来所以需要记录状态机的状态以及上次check到的位置。在解析完HTTP报文后还需要保存解析的结果然后根据解析结果来产生相应response。 代码结构HttpServer基于线程池和epoll实现的Reactor模式的简单HTTP服务器threadpool.h基于C实现的线程池类的封装采用模板来实现拓展性locker.h封装了信号量、互斥锁、条件变量的使用http_conn.h封装了HTTP协议的解析方法比较繁琐main.cpp采用epoll实现了简单的Reactor模式主线程复杂接受连接线程池负责处理任务。】http_conn.h#ifndef HTTPCONNECTION_H
#define HTTPCONNECTION_H
#include unistd.h
#include signal.h
#include sys/types.h
#include sys/epoll.h
#include sys/uio.h
#include fcntl.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include assert.h
#include sys/stat.h
#include string.h
#include pthread.h
#include stdio.h
#include stdlib.h
#include sys/mman.h
#include stdarg.h
#include errno.h
#include locker.h
class http_conn
{
public:
static const int FILENAME_LEN 200;
static const int READ_BUFFER_SIZE 2048;
static const int WRITE_BUFFER_SIZE 1024;
enum METHOD { GET 0, POST, HEAD, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH };
enum CHECK_STATE { CHECK_STATE_REQUESTLINE 0, CHECK_STATE_HEADER, CHECK_STATE_CONTENT };
enum HTTP_CODE { NO_REQUEST, GET_REQUEST, BAD_REQUEST, NO_RESOURCE, FORBIDDEN_REQUEST, FILE_REQUEST, INTERNAL_ERROR, CLOSED_CONNECTION };
enum LINE_STATUS { LINE_OK 0, LINE_BAD, LINE_OPEN };
public:
http_conn(){}
~http_conn(){}
public:
void init( int sockfd, const sockaddr_in addr );
void close_conn( bool real_close true );
void process();
bool read();
bool write();
private:
void init();
HTTP_CODE process_read();
bool process_write( HTTP_CODE ret );
HTTP_CODE parse_request_line( char* text );
HTTP_CODE parse_headers( char* text );
HTTP_CODE parse_content( char* text );
HTTP_CODE do_request();
char* get_line() { return m_read_buf m_start_line; }
LINE_STATUS parse_line();
void unmap();
bool add_response( const char* format, ... );
bool add_content( const char* content );
bool add_status_line( int status, const char* title );
bool add_headers( int content_length );
bool add_content_length( int content_length );
bool add_linger();
bool add_blank_line();
public:
static int m_epollfd;
static int m_user_count;
private:
int m_sockfd;
sockaddr_in m_address;
char m_read_buf[ READ_BUFFER_SIZE ];
int m_read_idx;
int m_checked_idx;
int m_start_line;
char m_write_buf[ WRITE_BUFFER_SIZE ];
int m_write_idx;
CHECK_STATE m_check_state;
METHOD m_method;
char m_real_file[ FILENAME_LEN ];
char* m_url;
char* m_version;
char* m_host;
int m_content_length;
bool m_linger;
char* m_file_address;
struct stat m_file_stat;
struct iovec m_iv[2];
int m_iv_count;
};
#endif
http_conn.cpp#include http_conn.hconst char* ok_200_title OK;
const char* error_400_title Bad Request;
const char* error_400_form Your request has bad syntax or is inherently impossible to satisfy.\n;
const char* error_403_title Forbidden;
const char* error_403_form You do not have permission to get file from 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 requested file.\n;
const char* doc_root /var/www/html;int setnonblocking( int fd )
{
int old_option fcntl( fd, F_GETFL );
int new_option old_option | O_NONBLOCK;
fcntl( fd, F_SETFL, new_option );
return old_option;
}void addfd( int epollfd, int fd, bool one_shot )
{
epoll_event event;
event.data.fd fd;
event.events EPOLLIN | EPOLLET | EPOLLRDHUP;
if( one_shot )
{
event.events | EPOLLONESHOT;
}
epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, event );
setnonblocking( fd );
}void removefd( int epollfd, int fd )
{
epoll_ctl( epollfd, EPOLL_CTL_DEL, fd, 0 );
close( fd );
}void modfd( int epollfd, int fd, int ev )
{
epoll_event event;
event.data.fd fd;
event.events ev | EPOLLET | EPOLLONESHOT | EPOLLRDHUP;
epoll_ctl( epollfd, EPOLL_CTL_MOD, fd, event );
}int http_conn::m_user_count 0;
int http_conn::m_epollfd -1;void http_conn::close_conn( bool real_close )
{
if( real_close ( m_sockfd ! -1 ) )
{
//modfd( m_epollfd, m_sockfd, EPOLLIN );
removefd( m_epollfd, m_sockfd );
m_sockfd -1;
m_user_count--;
}
}void http_conn::init( int sockfd, const sockaddr_in addr )
{
m_sockfd sockfd;
m_address addr;
int error 0;
socklen_t len sizeof( error );
getsockopt( m_sockfd, SOL_SOCKET, SO_ERROR, error, len );
int reuse 1;
setsockopt( m_sockfd, SOL_SOCKET, SO_REUSEADDR, reuse, sizeof( reuse ) );
addfd( m_epollfd, sockfd, true );
m_user_count;init();
}void http_conn::init()
{
m_check_state CHECK_STATE_REQUESTLINE;
m_linger false;m_method GET;
m_url 0;
m_version 0;
m_content_length 0;
m_host 0;
m_start_line 0;
m_checked_idx 0;
m_read_idx 0;
m_write_idx 0;
memset( m_read_buf, \0, READ_BUFFER_SIZE );
memset( m_write_buf, \0, WRITE_BUFFER_SIZE );
memset( m_real_file, \0, FILENAME_LEN );
}http_conn::LINE_STATUS http_conn::parse_line()
{
char temp;
for ( ; m_checked_idx m_read_idx; m_checked_idx )
{
temp m_read_buf[ m_checked_idx ];
if ( temp \r )
{
if ( ( m_checked_idx 1 ) m_read_idx )
{
return LINE_OPEN;
}
else if ( m_read_buf[ m_checked_idx 1 ] \n )
{
m_read_buf[ m_checked_idx ] \0;
m_read_buf[ m_checked_idx ] \0;
return LINE_OK;
}return LINE_BAD;
}
else if( temp \n )
{
if( ( m_checked_idx 1 ) ( m_read_buf[ m_checked_idx - 1 ] \r ) )
{
m_read_buf[ m_checked_idx-1 ] \0;
m_read_buf[ m_checked_idx ] \0;
return LINE_OK;
}
return LINE_BAD;
}
}return LINE_OPEN;
}bool http_conn::read()
{
if( m_read_idx READ_BUFFER_SIZE )
{
return false;
}int bytes_read 0;
while( true )
{
bytes_read recv( m_sockfd, m_read_buf m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0 );
if ( bytes_read -1 )
{
if( errno EAGAIN || errno EWOULDBLOCK )
{
break;
}
return false;
}
else if ( bytes_read 0 )
{
return false;
}m_read_idx bytes_read;
}
return true;
}http_conn::HTTP_CODE http_conn::parse_request_line( char* text )
{
m_url strpbrk( text, \t );
if ( ! m_url )
{
return BAD_REQUEST;
}
*m_url \0;char* method text;
if ( strcasecmp( method, GET ) 0 )
{
m_method GET;
}
else
{
return BAD_REQUEST;
}m_url strspn( m_url, \t );
m_version strpbrk( m_url, \t );
if ( ! m_version )
{
return BAD_REQUEST;
}
*m_version \0;
m_version strspn( m_version, \t );
if ( strcasecmp( m_version, HTTP/1.1 ) ! 0 )
{
return BAD_REQUEST;
}if ( strncasecmp( m_url, http://, 7 ) 0 )
{
m_url 7;
m_url strchr( m_url, / );
}if ( ! m_url || m_url[ 0 ] ! / )
{
return BAD_REQUEST;
}m_check_state CHECK_STATE_HEADER;
return NO_REQUEST;
}http_conn::HTTP_CODE http_conn::parse_headers( char* text )
{
if( text[ 0 ] \0 )
{
if ( m_method HEAD )
{
return GET_REQUEST;
}if ( m_content_length ! 0 )
{
m_check_state CHECK_STATE_CONTENT;
return NO_REQUEST;
}return GET_REQUEST;
}
else if ( strncasecmp( text, Connection:, 11 ) 0 )
{
text 11;
text strspn( text, \t );
if ( strcasecmp( text, keep-alive ) 0 )
{
m_linger true;
}
}
else if ( strncasecmp( text, Content-Length:, 15 ) 0 )
{
text 15;
text strspn( text, \t );
m_content_length atol( text );
}
else if ( strncasecmp( text, Host:, 5 ) 0 )
{
text 5;
text strspn( text, \t );
m_host text;
}
else
{
printf( oop! unknow header %s\n, text );
}return NO_REQUEST;}http_conn::HTTP_CODE http_conn::parse_content( char* text )
{
if ( m_read_idx ( m_content_length m_checked_idx ) )
{
text[ m_content_length ] \0;
return GET_REQUEST;
}return NO_REQUEST;
}http_conn::HTTP_CODE http_conn::process_read()
{
LINE_STATUS line_status LINE_OK;
HTTP_CODE ret NO_REQUEST;
char* text 0;while ( ( ( m_check_state CHECK_STATE_CONTENT ) ( line_status LINE_OK ) )
|| ( ( line_status parse_line() ) LINE_OK ) )
{
text get_line();
m_start_line m_checked_idx;
printf( got 1 http line: %s\n, text );switch ( m_check_state )
{
case CHECK_STATE_REQUESTLINE:
{
ret parse_request_line( text );
if ( ret BAD_REQUEST )
{
return BAD_REQUEST;
}
break;
}
case CHECK_STATE_HEADER:
{
ret parse_headers( text );
if ( ret BAD_REQUEST )
{
return BAD_REQUEST;
}
else if ( ret GET_REQUEST )
{
return do_request();
}
break;
}
case CHECK_STATE_CONTENT:
{
ret parse_content( text );
if ( ret GET_REQUEST )
{
return do_request();
}
line_status LINE_OPEN;
break;
}
default:
{
return INTERNAL_ERROR;
}
}
}return NO_REQUEST;
}http_conn::HTTP_CODE http_conn::do_request()
{
strcpy( m_real_file, doc_root );
int len strlen( doc_root );
strncpy( m_real_file len, m_url, FILENAME_LEN - len - 1 );
if ( stat( m_real_file, m_file_stat ) 0 )
{
return NO_RESOURCE;
}if ( ! ( m_file_stat.st_mode S_IROTH ) )
{
return FORBIDDEN_REQUEST;
}if ( S_ISDIR( m_file_stat.st_mode ) )
{
return BAD_REQUEST;
}int fd open( m_real_file, O_RDONLY );
m_file_address ( char* )mmap( 0, m_file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0 );
close( fd );
return FILE_REQUEST;
}void http_conn::unmap()
{
if( m_file_address )
{
munmap( m_file_address, m_file_stat.st_size );
m_file_address 0;
}
}bool http_conn::write()
{
int temp 0;
int bytes_have_send 0;
int bytes_to_send m_write_idx;
if ( bytes_to_send 0 )
{
modfd( m_epollfd, m_sockfd, EPOLLIN );
init();
return true;
}while( 1 )
{
temp writev( m_sockfd, m_iv, m_iv_count );
if ( temp -1 )
{
if( errno EAGAIN )
{
modfd( m_epollfd, m_sockfd, EPOLLOUT );
return true;
}
unmap();
return false;
}bytes_to_send - temp;
bytes_have_send temp;
if ( bytes_to_send bytes_have_send )
{
unmap();
if( m_linger )
{
init();
modfd( m_epollfd, m_sockfd, EPOLLIN );
return true;
}
else
{
modfd( m_epollfd, m_sockfd, EPOLLIN );
return false;
}
}
}
}bool http_conn::add_response( const char* format, ... )
{
if( m_write_idx WRITE_BUFFER_SIZE )
{
return false;
}
va_list arg_list;
va_start( arg_list, format );
int len vsnprintf( m_write_buf m_write_idx, WRITE_BUFFER_SIZE - 1 - m_write_idx, format, arg_list );
if( len ( WRITE_BUFFER_SIZE - 1 - m_write_idx ) )
{
return false;
}
m_write_idx len;
va_end( arg_list );
return true;
}bool http_conn::add_status_line( int status, const char* title )
{
return add_response( %s %d %s\r\n, HTTP/1.1, status, title );
}bool http_conn::add_headers( int content_len )
{
add_content_length( content_len );
add_linger();
add_blank_line();
}bool http_conn::add_content_length( int content_len )
{
return add_response( Content-Length: %d\r\n, content_len );
}bool http_conn::add_linger()
{
return add_response( Connection: %s\r\n, ( m_linger true ) ? keep-alive : close );
}bool http_conn::add_blank_line()
{
return add_response( %s, \r\n );
}bool http_conn::add_content( const char* content )
{
return add_response( %s, content );
}bool http_conn::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( 400, error_400_title );
add_headers( strlen( error_400_form ) );
if ( ! add_content( error_400_form ) )
{
return false;
}
break;
}
case NO_RESOURCE:
{
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 );
if ( m_file_stat.st_size ! 0 )
{
add_headers( m_file_stat.st_size );
m_iv[ 0 ].iov_base m_write_buf;
m_iv[ 0 ].iov_len m_write_idx;
m_iv[ 1 ].iov_base m_file_address;
m_iv[ 1 ].iov_len m_file_stat.st_size;
m_iv_count 2;
return true;
}
else
{
const char* ok_string htmlbody/body/html;
add_headers( strlen( ok_string ) );
if ( ! add_content( ok_string ) )
{
return false;
}
}
}
default:
{
return false;
}
}m_iv[ 0 ].iov_base m_write_buf;
m_iv[ 0 ].iov_len m_write_idx;
m_iv_count 1;
return true;
}void http_conn::process()
{
HTTP_CODE read_ret process_read();
if ( read_ret NO_REQUEST )
{
modfd( m_epollfd, m_sockfd, EPOLLIN );
return;
}bool write_ret process_write( read_ret );
if ( ! write_ret )
{
close_conn();
}modfd( m_epollfd, m_sockfd, EPOLLOUT );
}makefile httpserver: main.cpp http_conn.cpp threadpool.hg main.cpp http_conn.cpp threadpool.h -o httpserver -pthread
clean:rm httpserver
运行make执行 ./httpserver 0.0.0.0 80代码可在后台回复 “http服务器”作者会提供一个url下载链接