zoukankan      html  css  js  c++  java
  • http server 简单实现

    本blog主要是模仿http server的实现,使得客户端使用浏览器访问server所指定的目录。

    • 当访问的为一个目录时, 则列出改目录下的所有文件
    • 当访问的是文件时,则下载文件到本地

    本log仅仅做为httpd server的测试版本,功能不健全,学习而已!

    1. 效果图

    1.1 整体图:

    mark

    1.2 目录:

    要查看,不能直接点击,要在搜索栏中输入绝对路径,如 http://192.168.58.128/log/
    mark

    1.3 文件:

    要下载文件,不能直接点击,要在搜索栏中输入绝对路径,如 http://192.168.58.128/log/syslog
    mark

    1.4 错误:

    mark

    2. 预备知识

    欲学会本log所述内容,必须具备一下的基础知识:

    • 守候进程:

    基本概念:https://www.cnblogs.com/z-sm/p/5675051.html
    编程API:https://blog.csdn.net/lianghe_work/article/details/47659889

    • sscanf()函数:

    函数详解:http://www.cnblogs.com/Jimmy1988/p/8900440.html

    • 日志操作:

    概念及操作:http://www.cnblogs.com/Jimmy1988/p/8892483.html

    • **socket 编程: **

    基础知识:http://www.cnblogs.com/Jimmy1988/p/7839940.html
    socket API: http://www.cnblogs.com/Jimmy1988/p/7895213.html

    3. 源码地址

    本blog的源码放到了本人的github上了,包括项目源码/makefile/配置文件等。

    github 地址如下:

    https://github.com/Jimmy-Nie/httpd-server-.git

    4. 源码展示

    本文源码分为三个文件:

    • deamon.c: 各种函数功能的实现
    • http.c: main函数
    • http.h: 头文件

    4.1 源码

    ①. http.h

    /**********************************************************************************************
    *Copyright (C), 2018 ,Jimmy_Nie.  https://www.cnblogs.com/Jimmy1988/
    *
    *
    *File name: httpd.c
    *Description:实现httpd功能
    * Author		Date		Modify 
    * Jimmy			2018-04-16	Create
    *
    **********************************************************************************************/
    #ifndef HTTP_H
    #define HTTP_H
    
    #include <arpa/inet.h>
    #include <dirent.h>
    
    #include <errno.h>
    #include <fcntl.h>
    #include <linux/if.h>
    #include <netinet/in.h>
    
    #include <pwd.h>
    #include <grp.h>
    
    #include <stdio.h>
    #include <sys/socket.h>
    #include <syslog.h>
    #include <stdlib.h>
    #include <string.h>
    #include <strings.h>
    #include <sys/types.h>
    #include <sys/param.h>
    #include <sys/ioctl.h>
    #include <sys/socket.h>
    #include <sys/stat.h>
    #include <signal.h>
    #include <stdarg.h>
    
    #include <time.h>
    #include <unistd.h>
    
    /*********************************全局变量***************************************/
    extern char home_dir[32];
    extern char ip_addr[16];
    extern char port_no[8];
    extern char backlog[8];
    
    #define _BSD_SOURCE
    
    /***********************************************************
     * Function: write_info
     * Input:
     * Output: 
     * Return: 
     * Autor		Date			Modify
     * Jimmy		2018-04-16		Create
     * 
     ***********************************************************/
    #define write_info(fmt, arg...)	
    {														
    	char buff[512]; char buff1[256];					
    	bzero(buff, sizeof(buff));							
    	bzero(buff1, sizeof(buff));							
    	sprintf(buff, "<%s:%d> ", __FUNCTION__, __LINE__);	
    	sprintf(buff1, fmt, ##arg);							
    	strcat(buff, buff1);								
    	syslog(LOG_INFO, "%s", buff);						
    }
    
    
    /*********************************函数声明***************************************/
    extern void init_daemon(char *p_name, int facility);
    extern  int init_socket(int *p_sockfd);
    extern int get_ip_addr(char *ip_addr);
    extern void response_client(int client_sock, char *path);
    extern int read_conf(char *cmd, char *data);
    
    
    #endif
    
    

    ②. http.c

    /**********************************************************************************************
    *Copyright (C), 2018 ,Jimmy_Nie.  https://www.cnblogs.com/Jimmy1988/
    *
    *
    *File name: httpd.c
    *Description:实现httpd功能
    * Author		Date		Modify 
    * Jimmy			2018-04-16	Create
    *
    **********************************************************************************************/
    #include "http.h"
    
    char home_dir[32];
    char ip_addr[16];
    char port_no[8];
    char backlog[8];
    
    /***********************************************************
     * Function: main函数
     * Input:
     * Output: 
     * Return: 
     * Autor		Date			Modify
     * Jimmy		2018-04-16		Create
     * 
     ***********************************************************/
    int main(int argc, char *argv[])
    {
    	int rtn = 0;
    	int sock_fd = -1;
    	struct sockaddr_in client_addr;
    	char buf[256];
    	pid_t pid = -1;
    	
    	//1. 以守候进程的方式运行此http进程
    	init_daemon(argv[0], LOG_INFO);
    
    	//2. 获取参数值
    	//2.1 获取home_dir, 如果配置文件没有,则默认为/tmp
    	rtn = read_conf("home_dir", home_dir);
    	if(rtn < 0)
    		exit(EXIT_FAILURE);
    	else if(0 == rtn)	//即未从conf文件中读取到数据
    	{
    		bzero(home_dir, sizeof(home_dir));
    		strcpy(home_dir, "/tmp");
    	}
    
    	//2.2 获取ip_addr, 如果配置文件没有,则设置为本机的wlan0的ip地址
    	rtn = read_conf("ip_addr", ip_addr);
    	if(rtn < 0)
    		exit(EXIT_FAILURE);
    	else if(0 == rtn)	//即未从conf文件中读取到数据
    	{
    		bzero(ip_addr, sizeof(ip_addr));
    		get_ip_addr(ip_addr);
    	}
    
    	//2.3 获取port_no, 如果配置文件没有,则默认为80
    	rtn = read_conf("port_no", port_no);
    	if(rtn < 0)
    		exit(EXIT_FAILURE);
    	else if(0 == rtn)	//即未从conf文件中读取到数据
    	{
    		bzero(port_no, sizeof(port_no));
    		strcpy(port_no, "80");
    	}
    
    	//2.4 获取port_no, 如果配置文件没有,则默认为10(最多链接10个client)
    	rtn = read_conf("backlog", backlog);
    	if(rtn < 0)
    		exit(EXIT_FAILURE);
    	else if(0 == rtn)	//即未从conf文件中读取到数据
    	{
    		bzero(backlog, sizeof(backlog));
    		strcpy(backlog, "10");
    	}
    
    	write_info("home_dir=%s, ip_addr=%s, port_no=%d, backlog=%d
    ", home_dir, ip_addr, atoi(port_no), atoi(backlog));
    	
    	//3. 初始化socket
    	rtn = init_socket(&sock_fd);
    	if(rtn < 0)
    		exit(EXIT_FAILURE);
    
    	//4. 接收新的client链接
    	while(1)
    	{
    		int len;
    		int new_fd = -1;
    
    		len = sizeof(struct sockaddr_in);;
    		memset(&client_addr, 0, len);
    		new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &len);
    
    		if(new_fd < 0)
    		{
    			write_info("accept new client connection failed !! [%d:%s] 
    ", errno, strerror(errno));	
    			exit(EXIT_FAILURE);
    		}
    
    		bzero(buf, sizeof(buf));
    		sprintf(buf, "Connect from %s:%d
    ", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
    		write_info("%s
    ", buf);
    
    		if((pid = fork()) == -1)
    		{
    			write_info("fork() failed");
    			exit(EXIT_FAILURE);
    		}
    		
    		else if(0 == pid)	//在子进程中
    		{
    			bzero(buf, sizeof(buf));
    			len = recv(new_fd, buf, sizeof(buf), 0);
    			if(len > 0)		//接到了信息
    			{
    				char req[256];
    				bzero(req, sizeof(req));
    
    				sscanf(buf, "GET %s HTTP", req);
    				bzero(buf, sizeof(buf));
    				sprintf(buf, "Request get the file: %s", req);
    				write_info("%s
    ", buf);
    				response_client(new_fd, req);
    			}
    			sleep(30);	//建议使用chrome,firefox和IE貌似一直在请求,一旦tcp链接断开,就GG了
    			exit(EXIT_SUCCESS);
    		}
    		
    		else 	//在父进程中
    		{
    			close(new_fd);
    			continue;
    		}
    	}
    
    	close(sock_fd);
    	return 0;
    }
    

    ③. deamon.c

    /**********************************************************************************************
    *Copyright (C), 2018 ,Jimmy_Nie.  https://www.cnblogs.com/Jimmy1988/
    *
    *
    *File name: httpd.c
    *Description:实现httpd功能
    * Author		Date		Modify 
    * Jimmy			2018-04-16	Create
    *
    **********************************************************************************************/
    #include "http.h"
    
    /***********************************************************
     * Function: init_daemon
     * Input: 
     	1. char *p_name: 进程名称
     	2. int facility: 系统log进程的level
     * Output: 
     * Return: 
     * Autor		Date			Modify
     * Jimmy		2018-04-16		Create
     * 
     ***********************************************************/
    void init_daemon(char *p_name, int facility)
    {
    	int pid = 0;
    	int cnt = 0;
    
    	//1. 忽略所有可能的终端信号(守候进程不能受到终端的影响)
    	signal(SIGINT, SIG_IGN);	//终端中断符
    	signal(SIGTTOU, SIG_IGN);	//后台向控制端tty写作业
    	signal(SIGTSTP, SIG_IGN);	//终端挂起
    	signal(SIGHUP, SIG_IGN);	//链接断开
    	signal(SIGQUIT, SIG_IGN);	//终端退出
    
    	//2. 创建子进程,父进程推出(因为父进程由终端创建)
    	if((pid = fork()) > 0)		//在父进程中
    		exit(EXIT_SUCCESS);
    	else if(pid < 0)
    	{
    		printf("fork error: [%d:%s]", errno, strerror(errno));
    		exit(EXIT_FAILURE);
    	}
    
    	//3. 设置新的会话组长,新进程组长,脱离终端
    	setsid();
    
    	//4. 再次创建一个子进程,并让现在的父进程退出
    	if((pid = fork()) > 0)		//在父进程中
    		exit(EXIT_SUCCESS);
    	else if(pid < 0)
    	{
    		printf("fork error: [%d:%s]", errno, strerror(errno));
    		exit(EXIT_FAILURE);
    	}
    
    	//5. 修改子进程的主目录为/tmp
    	chdir("/tmp");
    
    	//6. 重置文件掩码
    	umask(0);
    
    	//7. 关闭所有父进程打开的文件描述符(包括stdin/stdout/stderr这三个; NOFILE为256)
    	for(cnt=0; cnt<NOFILE; cnt++)
    		close(cnt);
    
    	//8. 忽略子进程的退出信号
    	signal(SIGCHLD, SIG_IGN);
    
    	//9. 与syslogd守候进程关联
    	openlog(p_name, LOG_PID, facility);	//每个message都会增加pid进去
    	
    	return ;
    }
    
    
    
    /***********************************************************
     * Function: read_conf
     * Description: 读取配置文件,找出命令对应的值
     * Input:
     	1. char *cmd: 欲查找的命令
     * Output: 
     	1. char *data: 查找到的命令的值
     * Return: 
     	1. 命令所在字符串中的位置
     *
     * Autor		Date			Modify
     * Jimmy		2018-04-16		Create
     * 
     ***********************************************************/
    int read_conf(char *cmd, char *data)
    {
    	int fd = -2;
    	char buf[1024];
    	size_t rd_size;
    	char *match = NULL;
    	
    	memset(buf, 0, sizeof(buf));
    
    	//0. 检查传入的参数
    	if((NULL == cmd) || (NULL == data))
    	{
    		write_info("Input arguments error !");
    		return -1;
    	}
    	
    	//1. 打开配置文件
    	fd = open("/etc/httpd_test.conf", O_RDONLY);
    	if(fd < 0)	//注意,此处fd=0,因为关闭了stdin/stdout/stderr,所以fd应该从0开始 (TMD,排查了很久,以为出错了)
    	{
    		write_info("open the file /etc/httpd_test.conf failed! [fd=%d] [%d:%s]", fd, errno, strerror(errno));
    		return -1;
    	}
    
    	//2. 读取文件的所有数据
    	rd_size = read(fd, buf, sizeof(buf));
    	if((rd_size <= 0) || (rd_size == sizeof(buf)))
    	{
    		write_info("read the file /etc/httpd_test.conf failed! [%d:%s]", errno, strerror(errno));
    		return -1;
    	}
    	buf[rd_size] = '';
    	
    	//3. 关闭文件
    	close(fd);
    
    	//4. 参数匹配
    	if(strncmp(cmd, "home_dir", strlen("home_dir")) == 0)
    	{
    		match = strstr(buf, "home_dir=");
    		if(NULL == match)		//若未找到匹配项
    		{
    			write_info("Does not found the cmd[%s] in the file /etc/httpd_test.conf!", cmd);
    			return 0;
    		}
    
    		rd_size = sscanf(match, "home_dir=%s", data);
    		write_info("Found the command [%s], position=%ld
    ", cmd, rd_size);
    		return rd_size;
    	}
    
    	if(strncmp(cmd, "port_no", strlen("port_no")) == 0)
    	{
    		match = strstr(buf, "port_no=");
    		if(NULL == match)		//若未找到匹配项
    		{
    			write_info("Does not found the cmd[%s] in the file /etc/httpd_test.conf!", cmd);
    			return 0;
    		}
    
    		rd_size = sscanf(match, "port_no=%s", data);
    		write_info("Found the command [%s], position=%ld
    ", cmd, rd_size);
    		return rd_size;
    	}
    
    	if(strncmp(cmd, "ip_addr", strlen("ip_addr")) == 0)
    	{
    		match = strstr(buf, "ip_addr=");
    		if(NULL == match)		//若未找到匹配项
    		{
    			write_info("Does not found the cmd[%s] in the file /etc/httpd_test.conf!", cmd);
    			return 0;
    		}
    
    		rd_size = sscanf(match, "ip_addr=%s", data);
    		write_info("Found the command [%s], position=%ld
    ", cmd, rd_size);
    		return rd_size;
    	}
    
    	if(strncmp(cmd, "backlog", strlen("backlog")) == 0)
    	{
    		match = strstr(buf, "backlog=");
    		if(NULL == match)		//若未找到匹配项
    		{
    			write_info("Does not found the cmd[%s] in the file /etc/httpd_test.conf!", cmd);
    			return 0;
    		}
    
    		rd_size = sscanf(match, "backlog=%s", data);
    		write_info("Found the command [%s], position=%ld
    ", cmd, rd_size);
    		return rd_size;
    	}
    	
    	return 0;
    }
    
    /***********************************************************
     * Function: init_socket
     * Description: 初始化socket
     * Input:
     	1. char *p_sockfd: socket 文件描述符
     * Output: 
     * Return: 
     	0: on success
     	1:	on failure
     *
     * Autor		Date			Modify
     * Jimmy		2018-04-16		Create
     * 
     ***********************************************************/
     int init_socket(int *p_sockfd)
    {
    	int rtn =0;
    	struct sockaddr_in  serv_addr;
    	int sock_fd;
    	
    	//1. 建立socket
    	sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    	if(0 > sock_fd)
    	{
    		write_info("Create socket failed !! [%d:%s] ", errno, strerror(errno));
    		return -1;
    	}
    	write_info("socket()");
    	
    	//2. 设置socket允许复用本地IP和端口
    	int tmp = 1;
    	setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
    
    	//3. 绑定本地IP和端口
    	serv_addr.sin_family = AF_INET;
    	serv_addr.sin_port = htons(atoi(port_no));
    	serv_addr.sin_addr.s_addr = inet_addr(ip_addr);
    
    	rtn = bind(sock_fd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr_in));
    	if(rtn < 0)
    	{
    		write_info("Bind the IP addr [%s] failed !! [%d:%s] ", errno, ip_addr, strerror(errno));
    		return -1;
    	}
    	write_info("bind()");
    	
    	//4. 监听网络
    	rtn = listen(sock_fd, atoi(backlog));
    	if(rtn < 0)
    	{
    		write_info("Listen the network failed !! [%d:%s] ", errno, strerror(errno));
    		return -1;
    	}
    	write_info("listen()");
    
    	*p_sockfd = sock_fd;
    	
    	return rtn;
    }
    
     /***********************************************************
     * Function: get_ip_addr
     * Description: 初始化socket
     * Input:
     	1. char *p_addr: 本地IP地址
     	2. int port: 本地端口
     	3. int backlog: 最多可接受多少个链接
     * Output: 
     * Return: 
     	0: on success
     	1:	on failure
     *
     * Autor		Date			Modify
     * Jimmy		2018-04-16		Create
     * 
     ***********************************************************/
    int get_ip_addr(char *ip_addr)
    {
    	int sock_fd; 
    	struct ifreq ifr;
    
    	sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    
    	strcpy(ifr.ifr_name, "ens33");		//注意此处的修改,不同的系统名称不一样,一般为eth0
    
    	//获取ens33的接口信息
    	if(ioctl(sock_fd, SIOCGIFADDR, &ifr) < 0)
    	{
    		write_info("bind");
    		return -1;
    	}
    
    	sprintf(ip_addr, "%s", inet_ntoa(((struct sockaddr_in *)&(ifr.ifr_addr))->sin_addr));
    
    	return 0;
    }
    
    /***********************************************************
    * Function: file_type
    * Description: 获取文件的类型
    * Input:
       1. mode_t st_mode: 文件类型
    * Output: 
    * Return: 
    	文件类型的标识符
    *
    * Autor 	   Date 		   Modify
    * Jimmy 	   2018-04-17	   Create
    * 
    ***********************************************************/
    char file_type(mode_t st_mode)
    {
    	/*	only support c89
    	static char type;
    	switch(st_mode & S_IFMT)
    	{
    		case S_IFSOCK:
    			type = 's';
    			break;
    		case S_IFLNK:
    			type = 'l';
    			break;
    		case S_IFREG:
    			type = '-';
    			break;
    		case S_IFBLK:
    			type = 'b';
    			break;
    		case S_IFCHR:
    			type = 'c';
    			break;
    		case S_IFIFO:
    			type = 'p';
    			break;
    		case S_IFDIR:
    			type = 'd';
    			break;
    		default:
    			type = 'E';
    			break;
    	}
    
    	return type;
    	*/
    
    	if (S_ISREG(st_mode))
    		return '-';
    	else if (S_ISDIR(st_mode))
    		return 'd';
    	else if (S_ISCHR(st_mode))
    		return 'c';
    	else if (S_ISBLK(st_mode))
    		return 'b';
    	else if (S_ISLNK(st_mode))
    		return 'l';
    	else if (S_ISFIFO(st_mode))
    		return 'p';
    	//else if (S_ISSOCK(st_mode))
    		//return 's';
    	else
    		return '?';
    	
    }
    
    /***********************************************************
    * Function: dir_up
    * Description: 获取上级目录
    * Input:
       1. char *dir_path: 目录
    * Output: 
    * Return: 
    	文件类型的标识符
    *
    * Autor 	   Date 		   Modify
    * Jimmy 	   2018-04-17	   Create
    * 
    ***********************************************************/
    char *dir_up(char *dir_path)
    {
    	static char path[128];
    	int len;
    
    	strcpy(path, dir_path);
    	len = strlen(path);
    
    	if((len > 1) && (path[len-1] == '/'))
    		len--;
    	while((len > 1) && (path[len-1] != '/'))
    		len--;
    	
    	path[len] = '';
    	return path;
    }
    
    /***********************************************************
    * Function: response_client
    * Description: 响应客户端的请求:若为dir,则列出所有文件;若为file,则下载
    * Input:
       1. char *p_addr: 本地IP地址
       2. char *path:	文件的请求位置
    * Output: 
    * Return: 
       0: on success
       1:  on failure
    *
    * Autor 	   Date 		   Modify
    * Jimmy 	   2018-04-17	   Create
    * 
    ***********************************************************/
    void response_client(int client_sock, char *path)
    {
    	int rtn = 0;
    	int len = 0;
    	char real_path[64];
    	char file_name[128];
    	char tmp_port[8];
    	struct stat stat_info;
    	char send_msg[512];
    	DIR *dir = NULL;
    	int fd = -1;
    	struct dirent *dirt;
    	ssize_t send_size = 0;
    	char *p_time ;
    	struct passwd *p_user;
    	struct group *p_group;
    	
    	//0. 初始化变量
    	memset(real_path, 0, sizeof(real_path));
    	memset(tmp_port, 0, sizeof(tmp_port));
    	memset(send_msg, 0, sizeof(send_msg));
    	memset(file_name, 0, sizeof(file_name));
    	//memset(p_time, 0, sizeof(p_time));
    	
    	//1. 获取绝对路径
    	sprintf(real_path, "%s%s", home_dir, path);
    	
    	write_info("The real_path: %s
    ", real_path);
    	
    	//2. 获取端口信息(用于后续的输出)
    	//sprintf(tmp_port, " %s", port_no);
    
    	//3. 获取绝对路径的文件属性(文件还是目录,或其它)
    	rtn = stat(real_path, &stat_info);
    	if(0 != rtn)
    	{
    		write_info("Get the path[%s] state failed!! [%d:%s] ", real_path, errno, strerror(errno));
    		//将错误信息发送给client端
    		sprintf(send_msg, "HTTP/1.1 200 OK
    Server: Http test server
    Connection: close
    
    "	
    							"<html><head><title>%d - %s</title></head>"									
    							"<body><font size=+4>Linux http server</font><br><hr width="100%%"><br><center>"	
    							"<table border cols=3 width="100%%"></table><font_color="C0000" size=+2>"		
    							"connect to administrator, error code is 
    %s %s </font></body></html>",
    							errno, strerror(errno), path, strerror(errno));
    
    		send(client_sock, send_msg, strlen(send_msg)+1, 0);
    		if(send_size <= 0)
    		{
    			write_info("Send http header failed !!! 
    ");
    		}
    		else
    		{
    			write_info("Send http header succeed ! 
    ");
    		}
    		
    		return -1;
    	}
    
    	//如果请求的路径是一个文件,则将改文件直接发送给客户端
    	else if(S_ISREG(stat_info.st_mode))
    	{
    		write_info("The %s is a file! 
    ", real_path);
    		
    		fd = open(real_path, O_RDONLY);		//只读的方式打开
    		len = lseek(fd, 0, SEEK_END);		//查看打开文件的大小
    		lseek(fd, 0, SEEK_SET);				//将文件读写定位到开始位置
    
    		//开辟一个内存空间,用于存放读取到的文件信息
    		char *tmp_mem = (char *)malloc(len+1);
    		bzero(tmp_mem, len+1);
    
    		for(int i=0; i<len; i+=1024)	//读取文件,每次读 1k bytes
    		{
    			rtn = read(fd, tmp_mem+i, 1024);
    			if(rtn <= 0)
    				write_info("Read the file [%s] failed!! [%d:%s] ", real_path, errno, strerror(errno));
    		}
    
    		close(fd);		//读完之后,就可以关闭文件了
    
    		//将文件内容发送给客户端
    		memset(send_msg, 0, sizeof(send_msg));
    		sprintf(send_msg, "HTTP/1.1 200 OK
    Server: Http test server
    Connection keep alive
    "
    							"Content-type: application/*
    Content-length:%d
    
    ", len);
    		send_size = send(client_sock, send_msg, strlen(send_msg)+1, 0);
    		if(send_size <= 0)
    		{
    			write_info("Send http header failed !!! 
    ");
    		}
    		else
    		{
    			write_info("Send http header succeed ! 
    ");
    		}
    
    		send(client_sock, tmp_mem, len, 0);
    		if(send_size <= 0)
    		{
    			write_info("Send http message failed !!! 
    ");
    		}
    		else
    		{
    			write_info("Send http message succeed, len=%d ! 
    ", len);
    		}
    
    		sleep(20);
    		free(tmp_mem);
    	}
    	
    	//如果请求的路径是一个目录,将目录下的列表信息全部输出
    	else if(S_ISDIR(stat_info.st_mode))
    	{
    		write_info("The %s is a directory! 
    ", real_path);
    		
    		//将http协议信息发送给客户端,为了便于显示,先发送表格头
    		memset(send_msg, 0, sizeof(send_msg));
    		sprintf(send_msg, "HTTP/1.1 200 OK
    Server: Http test server
    Connection: close
    
    "	
    							"<html><head><title>Jimmy_Nie: %s</title></head>"									
    							"<body><font size=+4>Linux http server file - Jimmy</font><br><hr width="100%%"><br><center>"	
    							"<table border cols=3 width="100%%">"							
    							"<caption><font size=+3> Directory: %s</font></caption>
    "		
    							"<tr><td>Name</td><td>Type</td><td>Owner</td><td>Group</td>"	
    							"<td>Size</td><td>Modify time</td></tr>
    ",						
    							real_path, real_path);
    
    		send_size = send(client_sock, send_msg, strlen(send_msg)+1, 0);
    		if(send_size <= 0)
    		{
    			write_info("Send http header failed !!! 
    ");
    		}
    		else
    		{
    			write_info("Send http header succeed ! 
    ");
    		}
    
    		dir = opendir(real_path);	//打开绝对路径的目录
    		if(NULL == dir)		//若打开目录失败
    		{
    			write_info("Open the %s failed! 
    ", real_path);
    			memset(send_msg, 0, sizeof(send_msg));
    			sprintf(send_msg, "</table><font color="CC0000" size=+2>%s</font></body></html>", strerror(errno));
    
    			send_size = send(client_sock, send_msg, strlen(send_msg)+1, 0);
    			if(send_size <= 0)
    			{
    				write_info("Send http header failed !!! 
    ");
    			}
    			else
    			{
    				write_info("Send http header succeed ! 
    ");
    			}
    			
    			return -1;
    		}
    
    		while((dirt = readdir(dir)) != NULL)
    		{
    			if(dirt->d_name[0] == '.')	//即为隐藏文件
    				continue;
    			
    			send(client_sock, "<tr>", strlen("<tr>"), 0);
    			bzero(file_name, sizeof(file_name));
    			sprintf(file_name, "%s/%s", real_path, dirt->d_name);
    
    			write_info("opened the file %s !
    ", file_name);
    			
    			if(stat(file_name, &stat_info) == 0)	//读取文件信息
    			{
    				memset(send_msg, 0, sizeof(send_msg));
    				if(0 == strcmp(dirt->d_name, ".."))		//若为..,则要求到达上级目录
    					sprintf(send_msg, "<td><a href="http://%s:%s %s">(parent)</a></td>",
    										ip_addr, port_no, dir_up(path));
    				else
    					sprintf(send_msg, "<td><a href="http://%s:%s %s/%s">%s</a></td>",
    										ip_addr, port_no, path, dirt->d_name, dirt->d_name);
    
    				send_size = send(client_sock, send_msg, strlen(send_msg), 0);
    				if(send_size <= 0)
    				{
    					write_info("Send http header failed !!! 
    ");
    				}
    				else
    				{
    					write_info("Send http header succeed ! 
    ");
    				}
    
    				p_time = ctime(&stat_info.st_mtime);		//获取修改时间
    				p_user = getpwuid(stat_info.st_uid);		//获取文件拥有着
    				p_group = getgrgid(stat_info.st_gid);		//获取文件组信息
    
    				write_info("Time: %s, user: %s, group: %s
    ", p_time, p_user->pw_name, p_group->gr_name);
    				
    				//向客户端输出文件类型
    				memset(send_msg, 0, sizeof(send_msg));
    				sprintf(send_msg, "<td>%c</td>", file_type(stat_info.st_mode));
    				send_size = send(client_sock, send_msg, strlen(send_msg), 0);
    				if(send_size <= 0)
    				{
    					write_info("Send file type failed !!! 
    ");
    				}
    				else
    				{
    					write_info("Send file type succeed ! 
    ");
    				}
    
    				//向客户端输出文件所有者信息
    				memset(send_msg, 0, sizeof(send_msg));
    				sprintf(send_msg, "<td>%s</td>", p_user->pw_name);
    				send_size = send(client_sock, send_msg, strlen(send_msg), 0);
    				if(send_size <= 0)
    				{
    					write_info("Send file user failed !!! 
    ");
    				}
    				else
    				{
    					write_info("Send file user succeed ! 
    ");
    				}
    				
    				//向客户端输出文件组信息
    				memset(send_msg, 0, sizeof(send_msg));
    				sprintf(send_msg, "<td>%s</td>", p_group->gr_name);
    				send_size = send(client_sock, send_msg, strlen(send_msg), 0);
    				if(send_size <= 0)
    				{
    					write_info("Send file group failed !!! 
    ");
    				}
    				else
    				{
    					write_info("Send file group succeed ! 
    ");
    				}
    				
    				//向客户端输出文件大小
    				memset(send_msg, 0, sizeof(send_msg));
    				sprintf(send_msg, "<td>%d</td>", stat_info.st_size);
    				send_size = send(client_sock, send_msg, strlen(send_msg), 0);
    				if(send_size <= 0)
    				{
    					write_info("Send file size failed !!! 
    ");
    				}
    				else
    				{
    					write_info("Send file size succeed ! 
    ");
    				}
    				
    				//向客户端输出修改时间
    				char tmp_send[512];
    				memset(send_msg, 0, sizeof(send_msg));		//此处很奇怪,用send_message就发不出去
    				memset(tmp_send, 0, sizeof(tmp_send));
    				sprintf(tmp_send, "<td>%s</td>", p_time);
    								
    				send_size = send(client_sock, tmp_send, strlen(tmp_send), 0);
    				if(send_size <= 0)
    				{
    					write_info("Send file modify time failed !!! 
    ");
    				}
    				else
    				{
    					write_info("Send file modify time succeed ! 
    ");
    				}
    			}
    			
    			send(client_sock, "</tr>
    ", strlen("</tr>
    "), 0);
    		}
    		
    		send(client_sock, "</table></center></body></html>", strlen("</table></center></body></html>"), 0);
    	}
    
    	else	//如果木有权限访问
    	{
    		memset(send_msg, 0, sizeof(send_msg));
    		sprintf(send_msg, "HTTP/1.1 200 OK
    Server: Http test server
    Connection: close
    
    "	
    							"<html><head><title>%s</title></head>"									
    							"<body><font size=+4>Linux http server file</font><br><hr width="100%%"><br><center>"	
    							"<table border cols=3 width="100%%"></table>"							
    							"<font color="CC0000" size=+2>[%s]Permission denied, please contact the admin"
    							"</font></body></html>",	path);
    
    		send_size = send(client_sock, send_msg, strlen(send_msg)+1, 0);
    		if(send_size <= 0)
    		{
    			write_info("Send http header failed !!! 
    ");
    		}
    		else
    		{
    			write_info("Send http header succeed ! 
    ");
    		}
    	}
    	
    	return ;
    }
    
    
    

    4.2 makefile

    #==========================================
    # Copyright @ Jimmy 2018-04-21
    # httpd makefile
    # e-mail: JimmyNie2017@163.com
    #==========================================
    
    SHELL = /bin/bash
    
    SRC_DIR = ./code
    INC_DIR = ./code
    
    SRC_FILE := $(shell find $(SRC_DIR) -name '*.c')
    SRC_FILE += $(shell find $(SRC_DIR) -name '*.cpp')
    
    INC_FILE := $(shell find $(INC_DIR) -name '*.h')
    INC_FILE += $(shell find $(INC_DIR) -name '*.hpp')
    
    TEMP_INC_DIR := $(dir $(INC_FILE))
    INC_DIR := $(foreach tmp, $(TEMP_INC_DIR), -I$(tmp))
    
    OBJS = $(patsubst %.c, %.o, $(patsubst %.cpp, %.o, $(SRC_FILE)))
    
    # Target with the date time
    TARGET := httpd-$(shell date --rfc-3339='date')
    
    #compile tools
    CC 	:= gcc
    CXX := g++
    
    #libs as
    LIBS := -lpthread
    LIBS +=
    
    #compile flags 
    CFLAGS := -w -Wall -g -O0 
    CFLAGS += $(INC_DIR)
    CXXFLAGS := -std=c++0x $(CFLAGS)
    CFLAGS += -std=c99 
    
    #=========================================================
    all: $(TARGET)
    
    SEE:
    	@echo -e "SRC_FILE: $(SRC_FILE)"
    	@echo -e "OBJS: $(OBJS)"
    $(TARGET):$(OBJS) 
    	@$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
    	@echo  -e "33[31m33[1m make $@ done. 33[0m";	#red
    
    %.o:%.c
    	@$(CC) $(CFLAGS) -c $^ -o $@
    	@echo -e "33[32m33[1m make $@ done. 33[0m";	#green
    
    %.o:%.cpp
    	@$(CXX) $(CXXFLAGS) -c $^ -o $@
    	@echo -e "33[32m33[1m make $@ done. 33[0m";#green
    
    clean:
    	@rm -f $(TARGET])
    	@echo  -e "33[31m33[1m remove $(TARGET) done. 33[0m";	#red
    
    cleanall:
    	@rm -f $(TARGET)
    	@rm -f $(OBJS)
    	@echo  -e "33[31m33[1m Removed $(TARGET) done. 33[0m";	#red
    	@echo -e "33[32m33[1m Removed: $(OBJS) done. 33[0m";		#yellow
    
    install:
    	@$(shell rm -f /usr/bin/httpd-2018*)
    	@echo -e "Removed the old version"
    	@$(shell cp $(TARGET) /usr/bin/$(TARGET))
    	@$(shell cp ./httpd_test.conf /etc/httpd_test.conf)
    	@echo -e "33[32m33[1m copy $(TARGET) to /usr/bin/$(TARGET) 33[0m";	#green
    	@echo -e "33[32m33[1m copy httpd_test.conf to /etc/httpd_test.conf 33[0m";		#green
    
    
    

    4.3 配置文件

    #--------------------------------------------
    # Copyright @ 2018-04-17
    # This is httpd configure file
    # Autor: JImmy_Nie
    #--------------------------------------------
    
    home_dir=/var
    ip_addr         #use the local ip address
    port_no=80
    backlog=8
    
    

    4.4 一些结构体定义

    #include <linux/if.h>
    
    /*
     * Interface request structure used for socket
     * ioctl's.  All interface ioctl's must have parameter
     * definitions which begin with ifr_name.  The
     * remainder may be interface specific.
     */
    
    /* for compatibility with glibc net/if.h */
    #if __UAPI_DEF_IF_IFREQ
    struct ifreq {
    #define IFHWADDRLEN     6
            union
            {
                    char    ifrn_name[IFNAMSIZ];            /* if name, e.g. "en0" */
            } ifr_ifrn;
    
            union {
                    struct  sockaddr ifru_addr;
                    struct  sockaddr ifru_dstaddr;
                    struct  sockaddr ifru_broadaddr;
                    struct  sockaddr ifru_netmask;
                    struct  sockaddr ifru_hwaddr;
                    short   ifru_flags;
                    int     ifru_ivalue;
                    int     ifru_mtu;
                    struct  ifmap ifru_map;
                    char    ifru_slave[IFNAMSIZ];   /* Just fits the size */
                    char    ifru_newname[IFNAMSIZ];
                    void *  ifru_data;
                    struct  if_settings ifru_settings;
            } ifr_ifru;
    };
    #endif /* __UAPI_DEF_IF_IFREQ */
    
    #define ifr_name        ifr_ifrn.ifrn_name      /* interface name       */
    #define ifr_hwaddr      ifr_ifru.ifru_hwaddr    /* MAC address          */
    #define ifr_addr        ifr_ifru.ifru_addr      /* address              */
    #define ifr_dstaddr     ifr_ifru.ifru_dstaddr   /* other end of p-p lnk */
    #define ifr_broadaddr   ifr_ifru.ifru_broadaddr /* broadcast address    */
    #define ifr_netmask     ifr_ifru.ifru_netmask   /* interface net mask   */
    #define ifr_flags       ifr_ifru.ifru_flags     /* flags                */
    #define ifr_metric      ifr_ifru.ifru_ivalue    /* metric               */
    #define ifr_mtu         ifr_ifru.ifru_mtu       /* mtu                  */
    #define ifr_map         ifr_ifru.ifru_map       /* device map           */
    #define ifr_slave       ifr_ifru.ifru_slave     /* slave device         */
    #define ifr_data        ifr_ifru.ifru_data      /* for use by interface */
    #define ifr_ifindex     ifr_ifru.ifru_ivalue    /* interface index      */
    #define ifr_bandwidth   ifr_ifru.ifru_ivalue    /* link bandwidth       */
    #define ifr_qlen        ifr_ifru.ifru_ivalue    /* Queue length         */
    #define ifr_newname     ifr_ifru.ifru_newname   /* New name             */
    #define ifr_settings    ifr_ifru.ifru_settings  /* Device/proto settings*/
    
    
    #include <sys/stat.h>
    
    struct stat {
        dev_t     st_dev;         /* ID of device containing file */
        ino_t     st_ino;         /* inode number */
        mode_t    st_mode;        /* protection */
        nlink_t   st_nlink;       /* number of hard links */
        uid_t     st_uid;         /* user ID of owner */
        gid_t     st_gid;         /* group ID of owner */
        dev_t     st_rdev;        /* device ID (if special file) */
        off_t     st_size;        /* total size, in bytes */
        blksize_t st_blksize;     /* blocksize for filesystem I/O */
        blkcnt_t  st_blocks;      /* number of 512B blocks allocated */
    
         /* Since Linux 2.6, the kernel supports nanosecond
          precision for the following timestamp fields.
         For the details before Linux 2.6, see NOTES. */
    
        struct timespec st_atim;  /* time of last access */
        struct timespec st_mtim;  /* time of last modification */
        struct timespec st_ctim;  /* time of last status change */
    
       #define st_atime st_atim.tv_sec      /* Backward compatibility */
       #define st_mtime st_mtim.tv_sec
       #define st_ctime st_ctim.tv_sec
    };
    
    
    #include <dirent.h>
    
    struct dirent {
        ino_t          d_ino;       /* inode number */
        off_t          d_off;       /* not an offset; see NOTES */
        unsigned short d_reclen;    /* length of this record */
        unsigned char  d_type;      /* type of file; not supported
                                       by all filesystem types */
        char           d_name[256]; /* filename */
    };
    
    
  • 相关阅读:
    使用Microsoft.DirectX和Microsoft.DirectX.Sound进行录音时遇到的异常
    一个奇怪的TextChanged事件
    正则表达式
    lambda详解
    AOP统一处理请求
    SpringBoot表单参数验证
    208道Java常见面试题
    Java100道基础面试题
    Java多线程面试题
    Java编码规范
  • 原文地址:https://www.cnblogs.com/Jimmy1988/p/8901841.html
Copyright © 2011-2022 走看看