• 【八】http服务器开发--实现一个http服务器


    一、整体概述

    本节主要实现一个http服务器的示例;该http服务器的主要功能是,在浏览器端访问服务器ip+html文件名,结果为如果服务端如果存在我们想要的html文件,则将其展示在浏览器端。

    整体流程下图所示:
    在这里插入图片描述
    主要步骤就三个,接收http请求,解析http请求,相应http请求。

    二、接收http请求

    这部分主要负责接收http请求,并对请求信息进行解析。

    从上一节中我们知道,客户端发来的请求信息,主要包括请求行,请求头部,请求内容等组成,
    在这里插入图片描述
    通过解析请求行我们可以得到请求方法和URL等,然后解析请求头部信息,那怎么判断请求头部信息结束了呢?由图中可以看出,当连续解析到两个回车符时,就是请求头部结束了,剩下的就是请求数据。

    首先是解析请求行,解析请求行的函数如下:

    //读取请求行:返回值: -1 表示读取出错, 等于0表示读到一个空行, 大于0 表示成功读取一行
    int get_line(int sock, char *buf, int size)
    {
    	int count = 0;//计数器,用来对当前已经读取的字符数进行计数
    	char ch = '\0';//字符串结束符
    	int len = 0;
    	
    	//当前读取的字符数小于传入的字符数size-1,且ch不是换行符时
    	while( (count<size - 1) && ch!='\n'){
    		len = read(sock, &ch, 1);//将sock里的数据读到ch中,每次读1个字符;当返回值len为1时,说明返回的是1,即读取成功
    		
    		if(len == 1)
            {
    			if(ch == '\r')//如果读到的是回车符,则continue,进行下一次循环
                {
    				continue;
    			}else if(ch == '\n')//如果读到的是换行符,说明这一行读完了,break跳出循环
                {
    				//buf[count] = '\0';
    				break;
    			}
    			
    			//这里处理一般的字符
    			buf[count] = ch;
    			count++;
    			
    		}
            else if( len == -1 )
            {//读取出错
    			perror("read failed");
    			count = -1;
    			break;
    		}
            else 
            {// read 返回0,客户端关闭sock 连接.
    			fprintf(stderr, "client close.\n");
    			count = -1;
    			break;
    		}
    	}
    	
    	if(count >= 0) 
            buf[count] = '\0';//为读取的这一行添加字符串结束符
    	
    	return count;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    解析完请求行后,我们需要拿出请求行中的URL,然后拿到URL中包含的html文件名,并在我们本地的html文件夹中搜索是否存在该URL对应的html文件。
    编写了一个名为do_http_request的函数去解析收到的请求信息,功能为解析请求拿到URL,并在本地路径中查找该html的功能,代码过程如下:

    void do_http_request(int client_sock)
    {
    	int len = 0;
    	char buf[256];
    	char method[64];//请求的方法,是get还是post
    	char url[256];//请求的URL
    	char path[256];
    	
    	struct stat  st;
    	
    	
    	/*读取客户端发送的http 请求*/
    	
    	
    	//1.读取请求行
    	len = get_line(client_sock, buf, sizeof(buf));//请求行只要一行,因此只需要读一次即可
        printf("--line 96,do_http_request --,result for get_line is [%s]\n",buf);
    	
    	if(len > 0)
        {   //读到了请求行,首先获取请求的方法
    		int i=0, j=0;
    		while(!isspace(buf[j]) && (i<sizeof(method)-1))//如果不是空格,且没超过method的容量最大值;遇到空格说明请求方法结束了
            {
    			method[i] = buf[j];//用来存放请求方法,是get还是post
    			i++;
    			j++;
    		}
    		
    		method[i] = '\0';
    		if(debug) printf("request method: %s\n", method);
    		
    		if(strncasecmp(method, "GET", i)==0)
            { //只处理get请求
    	        if(debug) printf("method = GET\n");
    		
    		    //获取url
    		    while(isspace(buf[j++]));//跳过白空格
    		    i = 0;
    		
    		    while(!isspace(buf[j]) && (i<sizeof(url)-1))//碰到空格则跳出循环,即读到了完整的URL
                {
    			    url[i] = buf[j];//用来存放URL
    			    i++;
    			    j++;
    		    }
    		
    		    url[i] = '\0';
    		
    		    if(debug) printf("-----url is: [%s]\n", url);
    		
    			//继续读取http 头部
    		    do
                {
    			    len = get_line(client_sock, buf, sizeof(buf));
    			    if(debug) printf("read: %s\n", buf);
    			
    		    }while(len>0);
    			
    			//***定位服务器本地的html文件***
    			
    			//处理url 中的问号?,因为有的url是带着问号的,我们这里只取问号前面的作为URL
    			{
    				char *pos = strchr(url, '?');//查找问号第一次出现的位置
    				if(pos)
                    {
    					*pos = '\0';//将问号的地方换成\0,即字符串结束符,表示此URL到此结束
    					printf("-----real url: [%s]\n", url);
    				}
    			}
    			
    			sprintf(path, "./html_docs/%s", url);//./html_docs是我们存放html文件的文件夹路径
    			if(debug) printf("----path is: [%s]\n", path);
    			
    			//执行http 响应
    			//判断文件是否存在,如果存在就响应200 OK,同时发送相应的html 文件,如果不存在,就响应 404 NOT FOUND.
    			if(stat(path, &st)==-1)
                {//文件不存在或是出错
    				fprintf(stderr, "stat %s failed. reason: %s\n", path, strerror(errno));
    			    not_found(client_sock);
    			}
                else 
                {//文件存在
    			
    			    if(S_ISDIR(st.st_mode)){
    					strcat(path, "/index.html");
    				}
    				
    				do_http_response(client_sock, path);
    				
    			}
    				
    	
    	    }
            else 
            {//非get请求, 读取http 头部,并响应客户端 501 	Method Not Implemented
    			fprintf(stderr, "warning! other request [%s]\n", method);
    			do{
    			    len = get_line(client_sock, buf, sizeof(buf));
    			    if(debug) printf("read: %s\n", buf);
    			
    		    }while(len>0);
    			
    			//unimplemented(client_sock);   //在响应时再实现
    			
    		}
    		
    			
    		
    	}else {//请求格式有问题,出错处理
    		//bad_request(client_sock);   //在响应时再实现
    	}
    
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114

    三、响应http请求

    接收并解析客户端的请求后,如果找到客户端所要求请的内容,则需要对其进行响应。

    响应的格式如下所示:
    主要包括响应头部(状态行+消息报头)、响应正文两部分。

    在这里插入图片描述
    组装响应头部的实现代码如下所示:

    /****************************
     *返回关于响应文件信息的http 头部
     *输入: 
     *     client_sock - 客服端socket 句柄
     *     resource    - 文件的句柄 
     *返回值: 成功返回0 ,失败返回-1
    ******************************/
    int  headers(int client_sock, FILE *resource)
    {
    	struct stat st;
    	int fileid = 0;
    	char tmp[64];
    	char buf[1024]={0};
    	strcpy(buf, "HTTP/1.0 200 OK\r\n");
    	strcat(buf, "Server: anchenliang test Server\r\n");
    	strcat(buf, "Content-Type: text/html\r\n");
    	strcat(buf, "Connection: Close\r\n");
    	
    	fileid = fileno(resource);//获取该文件的file id
    	
    	if(fstat(fileid, &st)== -1)//同stat函数类似,查看st文件是否存在,返回-1则说明文件不存在的
        {
    		inner_error(client_sock);//发送500的错误码,表示服务器内部出错
    		return -1;
    	}
    	
    	snprintf(tmp, 64, "Content-Length: %ld\r\n\r\n", st.st_size);//连续两个回车符和换行符表示这是消息头部的结束了,st.st_size表示的是st文件的长度
    	strcat(buf, tmp);
    	
    	if(debug) fprintf(stdout, "header: %s\n", buf);
    	
    	if(send(client_sock, buf, strlen(buf), 0) < 0)//send函数表示向socket函数发送消息,返回值小于0表示发送失败了
        {
    		fprintf(stderr, "send failed. data: %s, reason: %s\n", buf, strerror(errno));
    		return -1;
    	}
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    组装响应内容的函数如下所示:

    /****************************
     *说明:实现将html文件的内容按行
            读取并送给客户端
     ****************************/
    void cat(int client_sock, FILE *resource)
    {
    	char buf[1024];
    	
        fgets(buf, sizeof(buf), resource);//将resource文件中的内容读到buf中,fgets函数每次读取一行
    	
    	while(!feof(resource))//feof用来判断resource文件是否读到尾部了,文件结束返回非0,文件未结束返回0
        {
    		int len = write(client_sock, buf, strlen(buf));//将buf的内容写进socket,即发送这一行内容
    		
    		if(len<0){//发送body 的过程中出现问题,怎么办?1.重试? 2.
    			fprintf(stderr, "send body error. reason: %s\n", strerror(errno));
    			break;
    		}
    		
    		if(debug) fprintf(stdout, "%s", buf);
    		fgets(buf, sizeof(buf), resource);
    		
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    四、完整代码即效果

    实现一个http服务器,功能为输入想要访问的html文件的URL,得到正确的响应html文件。下面附上完整代码以及实现过程;

    4.1 完整代码:

     #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    
    
    
    #define SERVER_PORT 80
    
    static int debug = 1;
    
    int get_line(int sock, char *buf, int size);
    void do_http_request(int client_sock);
    void do_http_response(int client_sock, const char *path);
    int  headers(int client_sock, FILE *resource);
    void cat(int client_sock, FILE *resource);
    
    void not_found(int client_sock);
    void inner_error(int client_sock);
    void do_http_response1(int client_sock);
    
    int main(void){
    
        int sock;//代表信箱
        struct sockaddr_in server_addr;
    
    
        //1.美女创建信箱
        sock = socket(AF_INET, SOCK_STREAM, 0);
    
        //2.清空标签,写上地址和端口号
        bzero(&server_addr, sizeof(server_addr));
    
        server_addr.sin_family = AF_INET;//选择协议族IPV4
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本地所有IP地址
        server_addr.sin_port = htons(SERVER_PORT);//绑定端口号
    
        //实现标签贴到收信得信箱上
        bind(sock, (struct sockaddr *)&server_addr,  sizeof(server_addr));
    
        //把信箱挂置到传达室,这样,就可以接收信件了
        listen(sock, 128);
    
        //万事俱备,只等来信
        printf("等待客户端的连接\n");
    
    
        int done =1;
    
        while(done){
            struct sockaddr_in client;
            int client_sock, len, i;
            char client_ip[64];
            char buf[256];
    
            socklen_t  client_addr_len;
            client_addr_len = sizeof(client);
            client_sock = accept(sock, (struct sockaddr *)&client, &client_addr_len);
    
            //打印客服端IP地址和端口号
            printf("client ip: %s\t port : %d\n",
                     inet_ntop(AF_INET, &client.sin_addr.s_addr,client_ip,sizeof(client_ip)),
                     ntohs(client.sin_port));
            /*处理http 请求,读取客户端发送的数据*/
            do_http_request(client_sock);
    		close(client_sock);
    
        }
        close(sock);
        return 0;
    }
    
    
    void do_http_request(int client_sock)
    {
    	int len = 0;
    	char buf[256];
    	char method[64];//请求的方法,是get还是post
    	char url[256];//请求的URL
    	char path[256];
    	
    	struct stat  st;//用于stat函数执行后,保存文件状态
    	
    	
    	/*读取客户端发送的http 请求*/
    	
    	
    	//1.读取请求行
    	len = get_line(client_sock, buf, sizeof(buf));//请求行只要一行,因此只需要读一次即可
        printf("--line 96,do_http_request --,result for get_line is [%s]\n",buf);
    	
    	if(len > 0)
        {   //读到了请求行,首先获取请求的方法
    		int i=0, j=0;
    		while(!isspace(buf[j]) && (i<sizeof(method)-1))//如果不是空格,且没超过method的容量最大值;遇到空格说明请求方法结束了
            {
    			method[i] = buf[j];//用来存放请求方法,是get还是post
    			i++;
    			j++;
    		}
    		
    		method[i] = '\0';
    		if(debug) printf("request method: %s\n", method);
    		
    		if(strncasecmp(method, "GET", i)==0)
            { //只处理get请求
    	        if(debug) printf("method = GET\n");
    		
    		    //获取url
    		    while(isspace(buf[j++]));//跳过白空格
    		    i = 0;
    		
    		    while(!isspace(buf[j]) && (i<sizeof(url)-1))//碰到空格则跳出循环,即读到了完整的URL
                {
    			    url[i] = buf[j];//用来存放URL
    			    i++;
    			    j++;
    		    }
    		
    		    url[i] = '\0';
    		
    		    if(debug) printf("-----url is: [%s]\n", url);
    		
    			//继续读取http 头部
    		    do
                {
    			    len = get_line(client_sock, buf, sizeof(buf));
    			    if(debug) printf("read: %s\n", buf);
    			
    		    }while(len>0);
    			
    			//***定位服务器本地的html文件***
    			
    			//处理url 中的问号?,因为有的url是带着问号的,我们这里只取问号前面的作为URL
    			{
    				char *pos = strchr(url, '?');//查找问号第一次出现的位置
    				if(pos)
                    {
    					*pos = '\0';//将问号的地方换成\0,即字符串结束符,表示此URL到此结束
    					printf("-----real url: [%s]\n", url);
    				}
    			}
    			
    			sprintf(path, "./html_docs/%s", url);//./html_docs是我们存放html文件的文件夹路径
    			if(debug) printf("----path is: [%s]\n", path);
                //do_http_response1(client_sock);
    			
    			//执行http 响应
    			//判断文件是否存在,如果存在就响应200 OK,同时发送相应的html 文件,如果不存在,就响应 404 NOT FOUND.
    			if(stat(path, &st)==-1)
                {//文件不存在或是出错
    				fprintf(stderr, "stat %s failed. reason: %s\n", path, strerror(errno));
    			    not_found(client_sock);
    			}
                else 
                {   //文件存在
    			
    			    if(S_ISDIR(st.st_mode))//S_ISDIR用来判断st是否是一个目录
                    {
    					strcat(path, "/index.html");//在path的字符串后面加上/index.html
    				}
    				
    				do_http_response(client_sock, path);
    				
    			}
    				
    	
    	    }
            else 
            {//非get请求, 读取http 头部,并响应客户端 501 	Method Not Implemented
    			fprintf(stderr, "warning! other request [%s]\n", method);
    			do{
    			    len = get_line(client_sock, buf, sizeof(buf));
    			    if(debug) printf("read: %s\n", buf);
    			
    		    }while(len>0);
    			
    			//unimplemented(client_sock);   //在响应时再实现
    			
    		}
    		
    			
    		
    	}else {//请求格式有问题,出错处理
    		//bad_request(client_sock);   //在响应时再实现
    	}
    	
    	
    	
    	
    	
    }
    
    /*响应http的函数*/
    void do_http_response(int client_sock, const char *path)
    {
    	int ret = 0;
    	FILE *resource = NULL;
    	
    	resource = fopen(path, "r");
    	
    	if(resource == NULL){
    		not_found(client_sock);
    		return ;
    	}
    	
    	//1.发送http 头部
    	ret = headers(client_sock, resource);
    	
    	//2.发送http body .
    	if(!ret){
    	    cat(client_sock, resource);
    	}
    	
    	fclose(resource);
    }
    
    /****************************
     *返回关于响应文件信息的http 头部
     *输入: 
     *     client_sock - 客服端socket 句柄
     *     resource    - 文件的句柄 
     *返回值: 成功返回0 ,失败返回-1
    ******************************/
    int  headers(int client_sock, FILE *resource)
    {
    	struct stat st;
    	int fileid = 0;
    	char tmp[64];
    	char buf[1024]={0};
    	strcpy(buf, "HTTP/1.0 200 OK\r\n");
    	strcat(buf, "Server: anchenliang test Server\r\n");
    	strcat(buf, "Content-Type: text/html\r\n");
    	strcat(buf, "Connection: Close\r\n");
    	
    	fileid = fileno(resource);//获取该文件的file id
    	
    	if(fstat(fileid, &st)== -1)//同stat函数类似,查看st文件是否存在,返回-1则说明文件不存在的
        {
    		inner_error(client_sock);//发送500的错误码,表示服务器内部出错
    		return -1;
    	}
    	
    	snprintf(tmp, 64, "Content-Length: %ld\r\n\r\n", st.st_size);//连续两个回车符和换行符表示这是消息头部的结束了,st.st_size表示的是st文件的长度
    	strcat(buf, tmp);
    	
    	if(debug) fprintf(stdout, "header: %s\n", buf);
    	
    	if(send(client_sock, buf, strlen(buf), 0) < 0)//send函数表示向socket函数发送消息,返回值小于0表示发送失败了
        {
    		fprintf(stderr, "send failed. data: %s, reason: %s\n", buf, strerror(errno));
    		return -1;
    	}
    	
    	return 0;
    }
    
    /****************************
     *说明:实现将html文件的内容按行
            读取并送给客户端
     ****************************/
    void cat(int client_sock, FILE *resource)
    {
    	char buf[1024];
    	
        fgets(buf, sizeof(buf), resource);//将resource文件中的内容读到buf中,fgets函数每次读取一行
    	
    	while(!feof(resource))//feof用来判断resource文件是否读到尾部了,文件结束返回非0,文件未结束返回0
        {
    		int len = write(client_sock, buf, strlen(buf));//将buf的内容写进socket,即发送这一行内容
    		
    		if(len<0){//发送body 的过程中出现问题,怎么办?1.重试? 2.
    			fprintf(stderr, "send body error. reason: %s\n", strerror(errno));
    			break;
    		}
    		
    		if(debug) fprintf(stdout, "%s", buf);
    		fgets(buf, sizeof(buf), resource);
    		
    	}
    }
    
    
    void do_http_response1(int client_sock)
    {
        const char *main_header = "HTTP/1.0 200 OK\r\nServer: http Server\r\nContent-Type: text/html\r\nConnection: Close\r\n";
    
    
        const char * welcome_content = "\
    \n\
    \n\
    \n\
    This is a test\n\
    \n\
    \n\
    
    \n\


    \n\

    大家好,欢迎学习网络编程



    \n\
    \n\ 尊姓大名: \n\
    芳龄几何: \n\


    \n\ \n\
    \n\
    \n\ \n\ "
    ; //1. 发送main_header int len = write(client_sock, main_header, strlen(main_header)); if(debug) fprintf(stdout, "... do_http_response...\n"); if(debug) fprintf(stdout, "write[%d]: %s", len, main_header); //2. 生成Content-Length char send_buf[64]; int wc_len = strlen(welcome_content); len = snprintf(send_buf, 64, "Content-Length: %d\r\n\r\n", wc_len);//两个回车换行符,表示结束 len = write(client_sock, send_buf, len);//发送Content-Length if(debug) fprintf(stdout, "write[%d]: %s", len, send_buf); len = write(client_sock, welcome_content, wc_len); if(debug) fprintf(stdout, "write[%d]: %s", len, welcome_content); } //读取请求行:返回值: -1 表示读取出错, 等于0表示读到一个空行, 大于0 表示成功读取一行 int get_line(int sock, char *buf, int size) { int count = 0;//计数器,用来对当前已经读取的字符数进行计数 char ch = '\0';//字符串结束符 int len = 0; //当前读取的字符数小于传入的字符数size-1,且ch不是换行符时 while( (count<size - 1) && ch!='\n'){ len = read(sock, &ch, 1);//将sock里的数据读到ch中,每次读1个字符;当返回值len为1时,说明返回的是1,即读取成功 if(len == 1) { if(ch == '\r')//如果读到的是回车符,则continue,进行下一次循环 { continue; }else if(ch == '\n')//如果读到的是换行符,说明这一行读完了,break跳出循环 { //buf[count] = '\0'; break; } //这里处理一般的字符 buf[count] = ch; count++; } else if( len == -1 ) {//读取出错 perror("read failed"); count = -1; break; } else {// read 返回0,客户端关闭sock 连接. fprintf(stderr, "client close.\n"); count = -1; break; } } if(count >= 0) buf[count] = '\0';//为读取的这一行添加字符串结束符 return count; } void not_found(int client_sock) { const char * reply = "HTTP/1.0 404 NOT FOUND\r\n\ Content-Type: text/html\r\n\ \r\n\ \r\n\ \r\n\ \r\n\ NOT FOUND\r\n\ \r\n\ \r\n\

    文件不存在!\r\n\

    The server could not fulfill your request because the resource specified is unavailable or nonexistent.\r\n\ \r\n\ "; int len = write(client_sock, reply, strlen(reply)); if(debug) fprintf(stdout, reply); if(len <=0){ fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno)); } } void inner_error(int client_sock) { const char * reply = "HTTP/1.0 500 Internal Sever Error\r\n\ Content-Type: text/html\r\n\ \r\n\ \r\n\ \r\n\ \r\n\ Inner Error\r\n\ \r\n\ \r\n\

    服务器内部出错.\r\n\ \r\n\ "; int len = write(client_sock, reply, strlen(reply)); if(debug) fprintf(stdout, reply); if(len <=0){ fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno)); } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434

    4.2 实现过程

    第一步:将以上代码复制进minihttp.c文件中,编译:gcc minihttp.c -o minihttp
    第二步:新建一个html_dock文件夹,里面放置一个test.html文件。文件内容如下所示:

    
    
    
    	
    	test
    	
    
    
    	

    恭喜,http服务器示例测试成功

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    第三步:运行minihttp文件,./minihttp
    第四步:在浏览器访问你的ip(或者域名)+test.html,示例:http://anchenliang.com/test.html,结果如下:
    在这里插入图片描述

    附(stat函数

    作用:
    返回文件的状态信息

    头文件

    #include 
    #include 
    #include 
    
    • 1
    • 2
    • 3

    函数原型

    int stat(const char *path, struct stat *buf);
    int fstat(int fd, struct stat *buf);
    int lstat(const char *path, struct stat *buf);
    
    • 1
    • 2
    • 3

    path:
    文件的路径
    buf:
    传入的保存文件状态的指针,用于保存文件的状态
    返回值
    成功返回0,失败返回-1,设置errno

    struct stat {
                   dev_t     st_dev;     /* ID of device containing file */
                   ino_t     st_ino;     /* inode number */
                   mode_t    st_mode;    /* S_ISREG(st_mode)  是一个普通文件  S_ISDIR(st_mode)  是一个目录*/
                   
                   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 */
                   time_t    st_atime;   /* time of last access */
                   time_t    st_mtime;   /* time of last modification */
                   time_t    st_ctime;   /* time of last status change */
               };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    蓝桥杯实战应用【算法知识篇】-分治法介绍及关键点解析
    Elasticsearch keyword 中的 ignore_above配置项
    (附源码)springboot农田灌溉设施管理系统 毕业设计 260931
    AOP相关
    智能时代的蜕变:人工智能发展历程
    【操作系统导论】机制:受限直接执行 | 中断处理 | 陷阱 | 协作方式 | 非协作方式 | 上下文切换
    中视频伙伴计划开通收益功能的方法和使用介绍
    Centos7中docker安装教程-详细版
    ES数据库简单查询
    ThreadLocal详解
  • 原文地址:https://blog.csdn.net/AnChenliang_1002/article/details/126533877