`

基于libevent的webserver的实现

阅读更多
主要实现了静态文件访问、记录访问日志、文件目录列表
编译脚本:
gcc -Wall   fasthttpd.c -o fasthttpd -levent

重启脚本:
[cod="shell"]
#!/bin/sh
ps -ef | grep fasthttpd | grep -v grep | awk '{print $2}' | xargs -t -i kill -9 {} >/dev/null 2>&1
$(pwd)/fasthttpd


#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <dirent.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>

// 引入 libevent 头文件
#include <event.h>
#include <evhttp.h>

#define HOST_IP "127.0.0.1"
#define HOST_PORT 8080
#define REQUEST_TIME_OUT 3

#define DOCUMENT_ROOT "www"
#define BUFF_MAX_LEN 20000
#define SERVER_NAME "fasthttpd"
#define ACCESS_LOG "logs/access.log"
#define DEBUG_LOG "logs/server.log" /* 服务器调试日志 */
#define ERROR_LOG "logs/error.log" /* 服务器错误日志 */

void parser(char *s,char res[][255]);
static char *strtoupper( char *s );
static long filesize(const char *filename);
//static int file_exists(const char *filename);
static void mime_content_type( const char *name, char *ret );
int WriteLog( const char *message, unsigned int message_type );
static int is_dir(const char *filename);

static unsigned short g_is_log        = 1;
static int g_log_fd                    = 0;
static char dir_root[20000];

struct http_req
{
	char method[20];
	char request_uri[500];
	char http_version[100];
	char client_ip[20];
	char request_time[2000];

} http_req_line;


// 请求处理模块
void http_handler(struct evhttp_request *req, void *arg)
{
    char buff[20000];
	char real_path[20000];
    char tmp[2000];
	char content_type[2000];
    int fd;
	unsigned int http_status_code;

	time_t timep;
	struct tm *m;
	struct stat info;
    
    DIR *dir;
    struct dirent *ptr;

    struct evbuffer *buf;
    buf = evbuffer_new();
    
    // 分析URL参数
    char *decode_uri = strdup((char*) evhttp_request_uri(req));
    //struct evkeyvalq http_query;
    //evhttp_parse_query(decode_uri, &http_query);
    //free(decode_uri);
    
	sprintf(http_req_line.request_uri,"%s",decode_uri);

    // 从http头中获取参数,如果是GET传输,这里就可以取得action的值
    //const char *request_value = evhttp_find_header(&http_query, "data");
    
	// 返回给浏览器的信息
    evhttp_add_header(req->output_headers, "Server", "fasthttp");
    //evhttp_add_header(req->output_headers, "Connection", "keep-alive");
    evhttp_add_header(req->output_headers, "Connection", "close");

	// 取得请求时间
	memset(&buff,0,sizeof(buff));
	time(&timep);
	m = localtime(&timep);
	sprintf(http_req_line.request_time,"%4d-%02d-%02d %02d:%02d:%02d",(1900+m->tm_year),(1+m->tm_mon),m->tm_mday,m->tm_hour,m->tm_min,m->tm_sec);

	// 获取请求的服务器文件路径
	memset(&real_path,0,sizeof(real_path));
	sprintf(real_path,"%s/%s%s",dir_root,DOCUMENT_ROOT,http_req_line.request_uri);



	if(stat(real_path,&info) == -1)
	{
		memset(&buff,0,sizeof(buff));

		if (errno == ENOENT)
		{
			evhttp_send_error(req,404,"HTTP/1.1 404 Not Found");
			sprintf(buff,"HTTP/1.1 404 Not Found\t%s\t%s\n",http_req_line.request_uri,http_req_line.request_time);
		}
		else if(access(real_path,R_OK) < 0)
		{
			evhttp_send_error(req,403,"HTTP/1.1 403 Forbidden");
			sprintf(buff,"HTTP/1.1 403 Forbidden\t%s\t%s\n",http_req_line.request_uri,http_req_line.request_time);
		}
		else
		{
			evhttp_send_error(req,500,"HTTP/1.1 500 Server Error");
			sprintf(buff,"HTTP/1.1 500 Server Error\t%s\t%s\n",http_req_line.request_uri,http_req_line.request_time);
		}

		evhttp_add_header(req->output_headers, "Content-Type", "text/html; charset=UTF-8");
		WriteLog(buff,0);
	}
	else if(S_ISREG(info.st_mode))
	{
		http_status_code = 200;
		mime_content_type(real_path,content_type);

		memset(&tmp,0,sizeof(tmp));
		fd = open(real_path,O_RDONLY);
		read(fd,tmp,filesize(real_path));
		close(fd);

		sprintf(buff,"%s; charset=UTF-8",content_type);

		// 记录访问日志
		memset(&buff,0,sizeof(buff));
		sprintf(buff,"HTTP/1.1 200 OK\t%s\t%ld\t%s\n",http_req_line.request_uri,filesize(real_path),http_req_line.request_time);
		WriteLog(buff,0);

		evhttp_add_header(req->output_headers, "Content-Type", buff);
		evbuffer_add_printf(buf, "%s", tmp);
		// 输出内容到浏览器
		evhttp_send_reply(req, HTTP_OK, "OK", buf);
	}
	else if(S_ISDIR(info.st_mode))
	{
		http_status_code = 200;
		memset(&tmp,0,sizeof(tmp));
		memset(&buff,0,sizeof(buff));
		sprintf(tmp,"<html><head><title>Index of %s</title></head><body><h1>Index of %s</h1><ul><li><a href=\"/\"> Parent Directory</a></li>",http_req_line.request_uri,http_req_line.request_uri);
		strcat(buff,tmp);

		if((dir = opendir(real_path)) != NULL)
		{
			while((ptr = readdir(dir)) != NULL)
			{
				if(strcmp(ptr->d_name,".") == 0 || strcmp(ptr->d_name,"..") == 0)
				{
					continue;    
				}
				 
				memset(&tmp,0,sizeof(tmp));
				sprintf(tmp,"%s/%s",real_path,ptr->d_name);
				
				if(is_dir(tmp))
				{
					memset(&tmp,0,sizeof(tmp));
					sprintf(tmp,"<li><a href=\"%s/\"> %s/</a></li>",ptr->d_name,ptr->d_name);
				}
				else
				{
					memset(&tmp,0,sizeof(tmp));
					sprintf(tmp,"<li><a href=\"%s\"> %s</a></li>",ptr->d_name,ptr->d_name);    
				}
				strcat(buff,tmp);
			}

			closedir(dir);
		}

		memset(&tmp,0,sizeof(tmp));
		sprintf(tmp,"%s","</ul>");
		strcat(buff,tmp);
		
		evhttp_add_header(req->output_headers, "Content-Type", "text/html; charset=UTF-8");
		evbuffer_add_printf(buf, "%s", buff);
		// 输出内容到浏览器
		evhttp_send_reply(req, HTTP_OK, "OK", buf);
	}
        
    // 内存释放
    //evhttp_clear_headers(&http_query);
    evbuffer_free(buf);
}

int main(int argc, char **argv)
{
    int timeout = 3;

	getcwd(dir_root,sizeof(dir_root));

    struct evhttp *httpd;
    event_init();

    // 绑定本机ip和端口,在访问时一定要把8080端口开放出来
    httpd = evhttp_start(HOST_IP, HOST_PORT);

    if (httpd == NULL) 
	{
        fprintf(stderr, "Error: Unable to listen on %s:%d\n\n", HOST_IP, HOST_PORT);        
        exit(1);        
    }

    // 设置请求超时时间
    evhttp_set_timeout(httpd, timeout);

    // 设置请求的处理函数
    evhttp_set_gencb(httpd, http_handler, NULL);
    event_dispatch();
    evhttp_free(httpd);

    return 0;
}

void parser(char *s,char res[][255])
{
    int i,j = 0;
    int n;
//    char hosts[255];

    for (i = 0;s[i] != '\r';i++)        /* obtain the first line in http protocol head */
        ;
    s[i] = '\0';
    n=i++;
    
    for (i = 0,j = 0;i < 3;i++,j++)        /* divide the protocol head in blank */
    {
        strcpy(res[j],strsep(&s," "));
    }
    
//    for(i=n;s[i] != '\r';i++)
//    {
//        strcat(hosts,s[i]);
//    }
//    
//    for (i = 0,j = 0;i < 3;i++,j++)        /* divide the protocol head in blank */
//    {
//        strcpy(host[j],strsep(&hosts,":"));
//    }
    
}

/**
 * strtoupper - string to upper
 *
 */
static char *strtoupper( char *s )
{
    int i, len = sizeof(s);
    for( i = 0; i < len; i++ )
    {
        s[i] = ( s[i] >= 'a' && s[i] <= 'z' ? s[i] + 'A' - 'a' : s[i] );
    }
    
    return(s);
}

/**
 *  filesize - get file size
 */
static long filesize(const char *filename)
{
    struct stat buf;
    if (!stat(filename, &buf))
    {
        return buf.st_size;
    }
    return 0;
}

/**
 * file_exists - check file is exist
 */
//static int file_exists(const char *filename)
//{
//    struct stat buf;
//    
//    if (stat(filename, &buf) < 0)
//    {
//        if (errno == ENOENT)
//        {
//            return 0;
//        }
//    }
//    return 1;
//}

/**
 * Get MIME type header
 *
 */
static void mime_content_type( const char *name, char *ret ){
    char *dot, *buf;

    dot = strrchr(name, '.');

    /* Text */
    if ( strcmp(dot, ".txt") == 0 ){
        buf = "text/plain";
    } else if ( strcmp( dot, ".css" ) == 0 ){
        buf = "text/css";
    } else if ( strcmp( dot, ".js" ) == 0 ){
        buf = "text/javascript";
    } else if ( strcmp(dot, ".xml") == 0 || strcmp(dot, ".xsl") == 0 ){
        buf = "text/xml";
    } else if ( strcmp(dot, ".xhtm") == 0 || strcmp(dot, ".xhtml") == 0 || strcmp(dot, ".xht") == 0 ){
        buf = "application/xhtml+xml";
    } else if ( strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0 || strcmp(dot, ".shtml") == 0 || strcmp(dot, ".hts") == 0 ){
        buf = "text/html";

    /* Images */
    } else if ( strcmp( dot, ".gif" ) == 0 ){
        buf = "image/gif";
    } else if ( strcmp( dot, ".png" ) == 0 ){
        buf = "image/png";
    } else if ( strcmp( dot, ".bmp" ) == 0 ){
        buf = "application/x-MS-bmp";
    } else if ( strcmp( dot, ".jpg" ) == 0 || strcmp( dot, ".jpeg" ) == 0 || strcmp( dot, ".jpe" ) == 0 || strcmp( dot, ".jpz" ) == 0 ){
        buf = "image/jpeg";

    /* Audio & Video */
    } else if ( strcmp( dot, ".wav" ) == 0 ){
        buf = "audio/wav";
    } else if ( strcmp( dot, ".wma" ) == 0 ){
        buf = "audio/x-ms-wma";
    } else if ( strcmp( dot, ".wmv" ) == 0 ){
        buf = "audio/x-ms-wmv";
    } else if ( strcmp( dot, ".au" ) == 0 || strcmp( dot, ".snd" ) == 0 ){
        buf = "audio/basic";
    } else if ( strcmp( dot, ".midi" ) == 0 || strcmp( dot, ".mid" ) == 0 ){
        buf = "audio/midi";
    } else if ( strcmp( dot, ".mp3" ) == 0 || strcmp( dot, ".mp2" ) == 0 ){
        buf = "audio/x-mpeg";
    } else if ( strcmp( dot, ".rm" ) == 0  || strcmp( dot, ".rmvb" ) == 0 || strcmp( dot, ".rmm" ) == 0 ){
        buf = "audio/x-pn-realaudio";
    } else if ( strcmp( dot, ".avi" ) == 0 ){
        buf = "video/x-msvideo";
    } else if ( strcmp( dot, ".3gp" ) == 0 ){
        buf = "video/3gpp";
    } else if ( strcmp( dot, ".mov" ) == 0 ){
        buf = "video/quicktime";
    } else if ( strcmp( dot, ".wmx" ) == 0 ){
        buf = "video/x-ms-wmx";
    } else if ( strcmp( dot, ".asf" ) == 0  || strcmp( dot, ".asx" ) == 0 ){
        buf = "video/x-ms-asf";
    } else if ( strcmp( dot, ".mp4" ) == 0 || strcmp( dot, ".mpg4" ) == 0 ){
        buf = "video/mp4";
    } else if ( strcmp( dot, ".mpe" ) == 0  || strcmp( dot, ".mpeg" ) == 0 || strcmp( dot, ".mpg" ) == 0 || strcmp( dot, ".mpga" ) == 0 ){
        buf = "video/mpeg";

    /* Documents */
    } else if ( strcmp( dot, ".pdf" ) == 0 ){
        buf = "application/pdf";
    } else if ( strcmp( dot, ".rtf" ) == 0 ){
        buf = "application/rtf";
    } else if ( strcmp( dot, ".doc" ) == 0  || strcmp( dot, ".dot" ) == 0 ){
        buf = "application/msword";
    } else if ( strcmp( dot, ".xls" ) == 0  || strcmp( dot, ".xla" ) == 0 ){
        buf = "application/msexcel";
    } else if ( strcmp( dot, ".hlp" ) == 0  || strcmp( dot, ".chm" ) == 0 ){
        buf = "application/mshelp";
    } else if ( strcmp( dot, ".swf" ) == 0  || strcmp( dot, ".swfl" ) == 0 || strcmp( dot, ".cab" ) == 0 ){
        buf = "application/x-shockwave-flash";
    } else if ( strcmp( dot, ".ppt" ) == 0  || strcmp( dot, ".ppz" ) == 0 || strcmp( dot, ".pps" ) == 0 || strcmp( dot, ".pot" ) == 0 ){
        buf = "application/mspowerpoint";

    /* Binary & Packages */
    } else if ( strcmp( dot, ".zip" ) == 0 ){
        buf = "application/zip";
    } else if ( strcmp( dot, ".rar" ) == 0 ){
        buf = "application/x-rar-compressed";
    } else if ( strcmp( dot, ".gz" ) == 0 ){
        buf = "application/x-gzip";
    } else if ( strcmp( dot, ".jar" ) == 0 ){
        buf = "application/java-archive";
    } else if ( strcmp( dot, ".tgz" ) == 0  || strcmp( dot, ".tar" ) == 0 ){
        buf = "application/x-tar";
    } else {
        buf = "application/octet-stream";
    }
    strcpy(ret, buf);
}

/**
 * Log message
 *
 */
int WriteLog( const char *message,unsigned int message_type )
{
    if ( !g_is_log )
    {
        fprintf(stderr, "%s", message);
        return 0;
    }

	char g_log_path[2000];

	getcwd(g_log_path, sizeof(g_log_path));
	strcat(g_log_path,"/");

	if(message_type == 0)
	{
		strcat(g_log_path,ACCESS_LOG);
	}
	else if(message_type == 1)
	{
		strcat(g_log_path,DEBUG_LOG);
	}
	else if(message_type == 2)
	{
		strcat(g_log_path,ERROR_LOG);
	}
	else
	{   
		perror("error message type");
		return -1;
	}

	if ( (g_log_fd = open(g_log_path, O_RDWR|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1 )
	{
		perror("open log file error");
		return -1;
	}
    
    if (write(g_log_fd, message, strlen(message)) == -1)
    {
        perror("write log error");
        return -1;
    }

    return 0;
}

/**
 * is_dir - check file is directory
 *
 */
static int is_dir(const char *filename){
    struct stat buf;
    if ( stat(filename, &buf) < 0 ){
        return -1;
    }
    if (S_ISDIR(buf.st_mode)){
        return 1;
    }
    return 0;
}

分享到:
评论

相关推荐

    libevent_webservice:基于libevent的简单异步多线程Webservice

    描述部分指出libevent_webservice是一个基于libevent的Web服务示例,并提供了进一步的信息获取途径——进入"webservice"目录查看安装、构建和运行的指导。这暗示了该项目包含一个结构化的代码仓库,用户可以通过标准...

    webserver底层实现

    本文将深入探讨Web服务器的底层实现,基于“WebServer_v14”这个文件名,我们可以推测这是一个基础版本的Web服务器实现,可能是用C++或者Java等编程语言编写的。 在Web服务器的设计中,以下几个关键知识点是至关...

    基于阿里云服务器+libevent+qt+mysql等实现仿qq聊天软件.zip

    自1998年首次发布以来,MySQL以其卓越的性能、可靠性和可扩展性,成为全球范围内Web应用程序、企业级解决方案以及其他各种数据处理场景的首选数据库平台之一。 以下是对MySQL数据库的详细介绍: 核心特性与优势 ...

    Fastdfs安装与配置

    下载文件支持HTTP协议,可基于内置Web Server或外部Web Server。支持在线扩容,动态添加卷。支持文件冗余备份和负载均衡。存储服务器上可以保存文件属性(meta-data)V2.0 网络通信采用libevent,支持大并发访问,...

    HTTP_Server.zip

    本项目是一个基于C语言实现的HTTP服务器,利用了Libevent库和OpenSSL库,实现了包括POST和GET请求、HTTPS支持、持久连接以及分块传输等多种功能,为理解HTTP服务器的工作原理提供了宝贵的实践平台。 首先,Libevent...

    基于apache的网络通信模型

    它基于libevent库,利用异步非阻塞I/O来提高服务器性能,尤其适合处理大量并发连接。Zevent通过监听套接字上的事件,例如连接请求、数据读写、连接关闭等,只在有实际活动时唤醒工作线程,降低了CPU的空转率。 在...

    计算机网络高级软件编程技术.rar

    7. **网络编程库与框架**:熟悉如Boost.Asio、libevent、libuv等网络编程库,以及基于这些库的Web框架(如Node.js、Python的Tornado)可以帮助简化开发过程,提高代码的可维护性。 8. **分布式系统**:在大规模网络...

    spserver:SPServer是一个用C++编写的高并发服务器框架库

    SPServer 是一个用 C++ 编写的服务器框架库,它实现了半同步/半异步和领导者/跟随者模式。 它基于 libevent,以便在任何平台上利用最佳 I/O 循环。 SPServer 可以简化 TCP 服务器的构建。 它是线程和事件驱动之间的...

    Memcached原理和使用详解

    - **基于libevent的事件处理机制**:为了实现高并发性能,Memcached内部采用了libevent库来处理客户端连接上的读写事件,libevent是一个高效的跨平台事件处理框架,支持多种操作系统,如Windows、Linux、BSD和...

    C++中通过Webservice和HTTP协议的网络传输

    5. **WebServer文件分析** 压缩包中的"WebServer"可能是一个简单的C++ Web服务器示例,用于演示如何接收和响应HTTP请求。这可能包括了监听TCP连接、解析HTTP请求、生成HTTP响应等功能。通过分析源代码,我们可以...

    C++ 实现WebSocket 服务器 可运行.zip

    在Web开发中,WebSocket常用于在线游戏、实时聊天、股票交易、在线协作等场景,因为这些应用都需要低延迟、高效率的数据交换。 本资源提供了一个用C++实现的WebSocket服务器,可以直接运行,这对于学习和理解...

    PHP实现WebSocket

    PHPAsyncWebSocketClient使用了PHP的异步和事件驱动编程模型,这通常是通过libevent或ReactPHP等底层库实现的。异步编程允许非阻塞I/O操作,提高程序并发性能。在WebSocket客户端中,这意味着当等待服务器响应时,...

    分布式文件系统FastDFS介绍

    - **与Web Server无缝衔接**:FastDFS已经提供了Apache和Nginx的扩展模块,便于集成。 - **支持多种文件大小**:无论是大文件还是海量小文件,FastDFS都能很好地支持。 - **内容去重**:支持相同的文件内容只保存一...

    Flask实现异步非阻塞请求功能实例解析

    Gevent是一个Python并发网络库,使用基于libevent事件循环的greenlet来提供一个高级同步API。Gevent可以帮助我们实现异步非阻塞请求功能。 四、monkey.patch_all()的作用? monkey.patch_all()是gevent库中的一个...

    Flash+gunicorn+django.docx

    4. **Gevent**:Gevent 是一个基于libevent和greenlet的Python库,用于编写高性能的异步网络程序。它利用了协程(coroutines)的概念,使得编写并发代码更加简单。 5. **CentOS**:这是一个流行的Linux发行版,常...

    memcached server

    Memcached基于键值对(key-value store)存储模式,它将数据存储在内存中,由于内存的读取速度远超硬盘,因此能够显著提高数据访问效率。当一个请求到达时,Memcached会根据键查找对应的值,如果找到,直接返回,...

    基于workerman做的Linux定时任务控制台(比linux自带的cronjob增加秒级功能).zip

    "server" =&gt; "127.0.0.1", // 需要访问的agent,一般是ip+8089 "client" =&gt; [ // '172.17.0.5:8089', '127.0.0.1:8089' ], "key" =&gt; "test", // 需要登录跳转的登录地址 "login_url" =&gt; ...

    RTCMultiConnection 基于webrtc的音视频流媒体服务器

    **RTCMultiConnection**是一款基于WebRTC技术实现的多人音视频通讯服务器,它允许用户进行一对一或者一对多的音视频聊天服务。这款服务器使用Node.js编写,适用于希望开发类似应用的研发团队作为参考案例。 #### 二...

    CTG-BSS_分布式WEB框架_操作手册V0.4 共58页.docx

    - 安装基础环境,如GCC、Libevent等。 - 编译安装Varnish。 - **配置要点**: - 配置Varnish以适应不同的应用场景,如缓存策略、后端服务器健康检查等。 #### 六、Session共享使用与说明 - **概述**:Session...

Global site tag (gtag.js) - Google Analytics