{C++系列} UNIX高级编程 项目 WEB服务器的实现
in C/C++ with 0 comment

{C++系列} UNIX高级编程 项目 WEB服务器的实现

in C/C++ with 0 comment

主框架

CF06DD82-C02E-48E1-B37E-40411C83F901.png

代码实现 server.c

/**************************
实现服务器的主程序
***************************/


#include<stdio.h>
#include "request.h"
#include<sys/types.h> 
#include "r_net.h"

int main(){
    struct sockaddr_in cli;
    int s_fd,conn_fd;
    request_info_t req; //解析到的请求头信息 struct request_info --- requests.h
    socklen_t cli_len = sizeof(cli);
    
    //创建一个socket
    s_fd = r_listen(9998,10);
    while(1){//阻塞等待客户端连接    
        conn_fd = accept(s_fd,(struct sockaddr *)&cli,&cli_len);
        if(conn_fd == -1){
            exit(0);
            perror("accept");
            return -1;
        }
        //创建子进程
        pid_t pid = fork();
        if(pid == -1){
            perror("fork");
            return -1;
        }
        if(pid == 0){//子进程
            close(s_fd);
            req = parse_requests(conn_fd);
            int res = get_response(conn_fd,&req);
            //response404(conn_fd);
            
            close(conn_fd);
        }else{//父进程
            close(conn_fd);
            waitpid(-1,NULL,WNOHANG);
        }
}
    return 0;
}


<!--more-->

DF18008A-70B4-4915-ABD8-3988E565FD3A.png

获取连接实现r_net.c

/**********************
本文件完成socket连接,服务器ip地址绑定等网络操作的封装 
**********************/

#include <sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<stdio.h>
/*
*获取服务器连接
*
***/
int r_listen(unsigned short port,int backlog){
    int s_fd;
    struct sockaddr_in serv;
    //创建一个socket
    s_fd = socket(AF_INET,SOCK_STREAM,0);
    if(s_fd == -1){
        perror("socket");
        return -1;
    }
    //初始化端口号和ip地址
    serv.sin_family = AF_INET;
    serv.sin_port = htons(port);
    serv.sin_addr.s_addr=htonl(INADDR_ANY);
    
    //将socket和ip和端口号绑定
    int b = bind(s_fd,(struct sockaddr *)&serv,sizeof(serv));
    if(b == -1 ){
        perror("bind");
        return -1;
    }
    //listen
    listen(s_fd,backlog);
    return s_fd;

}

C2361C9E-026F-40E8-A70C-86592C64D596.png

代码实现 parse_request.c

/**********************

本文件功能,解析客户端浏览器的请求
将关键数据提取出来
如请求文件 等

**********************/
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include "request.h"

//解析浏览器的请求信息   获取关键参数
request_info_t parse_requests(int conn_fd){
    char buf[1024];
    char *fname,*filetype,*method;
    request_info_t req;//将浏览器的请求头解析为一个结构体
    int r = read(conn_fd,buf,1024);
    //获取解析
    fname=get_filename(buf);
    filetype = get_filetype(buf);
    method = get_method(buf);    
    //将获取到的信息存储到结构体
    strcpy(req.filename,fname);
    strcpy(req.filetype,filetype);
    strcpy(req.method,method);
    //debug code
    printf("filename : %s\n",req.filename);
    printf("filetype: %s\n",req.filetype);
    printf("method: %s\n",req.method);    
    return req;
}

//获取文件名

char *get_filename(char *str){
    char buf[128];
        char *filename=(char *)malloc(128);;
    bzero(buf,128);
    char *start = strstr(str,"/");
    char *end = strstr(start," ");
    int num = end - start;
    memcpy(buf,start,num);
    strcpy(filename,buf);
    return filename;
}

//获取请求的文件类型
char *get_filetype(char *str){
    char *filetype = "text/html";
    return filetype;

}

//获取请求的方式GET  POST
char *get_method(char *str){
    char *method = "GET";
    return method;
}

返回包实现 get_response.c

/**************************
本文件主要给浏览器发送状态,文件信息

*************************/



#include<stdio.h>
#include "request.h"
#include<unistd.h>
#include<sys/types.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>

//给浏览器发送返回信息
int get_response(int fd,request_info_t *req){
    char buf[1024*64];//用来临时存放要返回的数据
    bzero(buf,1024*64);
    char file[64] = BASEDIR;//获取服务器根目录
    printf("request_info_t : req.filename:%s\n",req->filename);
    strcat(file,req->filename);
    printf("full filepath: %s\n",file);
    response_info_t res;
    get_code(&res,file);//获取状态码
    printf("res->status:%d\n",res.status);
    get_file_type(&res,req);//获取文件类型
    printf("res->filetype:%d\n\n\n",res.filetype);
    get_response_head(fd,&res);//发送返回http头
    if(res.status == 404){
        response404(fd);
    }else if(res.status == 403){
        response403(fd);
    }else{
        if(strstr(file,".") == NULL){//若请求的是目录,在后面追加index.html
            strcat(file,"/index.html");
        }
        int f_fd = open(file,O_RDONLY);
        if(f_fd == -1){
            perror("open");
            return -1;
        }
        while(1){
            int r = read(f_fd,buf,1024*64);
            if(r == -1){
                perror("read");
                return -1;
            }
            write(fd,buf,r);
            if(r==0) break;    
        }
    }    
    return 0;
}


//返回404信息
void response404(int fd){
         char *error_msg = "<h1>404 Not Found</h1><br><h2>drop server</h2>";
         write(fd,error_msg,strlen(error_msg));
 }

//返回403信息
void response403(int fd){
         char *error_msg = "<h1>403 Forbidden</h1><br><h2>drop server</h2>";
         write(fd,error_msg,strlen(error_msg));
 }


//获取文件状态码
static void get_code(response_info_t *res,char *filename){
    char buf[64];//临时存放文件路径及其名字
    bzero(buf,64);//清空
    strcpy(buf,filename);
    if(!strstr(buf,".")){
         strcat(buf,"/index.html");
        printf("buf: %s\n",buf);
        printf("access buf: %d\n",access(buf,F_OK|R_OK));
        get_code(res,buf);//递归获取返回状态码
    }else{
        if(!access(buf,F_OK|R_OK)){ //判断文件是否存在 ,存在,返回200  
            res->status = 200;
                printf("get_code  status %d\n",res->status);
            return;
        }
        else{
            res->status = 404;
                printf("get_code  status %d\n",res->status);
            return;
        }    
    }
}

//获取文件类型
static void get_file_type(response_info_t *res,request_info_t *req){
    if(strstr(req->filename,".")==NULL){
        res->filetype=1;
        return;
    }
    if(!strcmp(strrchr(req->filename,'.'),".gif") || !strcmp(strrchr(req->filename,'.'),".jpg")){
          res->filetype=2;
        return;
    }
    res->filetype=1;
    return;
}


//获取并发送http返回头
static void get_response_head(int fd,response_info_t *res){
    char *head_200="HTTP/1.1 200 ok\r\n";
    char *head_403="HTTP/1.1 403 Forbidden!\r\n";
    char *head_404="HTTP/1.1 404 Not Found!";
    
    char *type_text="Content-Type: text/html\r\n\r\n";
    char *type_jpeg="Content-Type: image/jpeg\r\n\r\n";
    switch(res->status){
        case 200:
            write(fd,head_200,strlen(head_200));
            break;
        case 403:
            write(fd,head_403,strlen(head_403));
            break;
        case 404:
            write(fd,head_404,strlen(head_404));
            break;
        default:
            break;

    }
    switch(res->filetype){
        case 1:
            write(fd,type_text,strlen(type_text));
            break;
        case 2:
            write(fd,type_jpeg,strlen(type_jpeg));
            break;
        default:
            write(fd,type_text,strlen(type_text));
            break;
    }
}


公共头文件 request.c

#ifndef REQUEST_H_
#define REQUEST_H_
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>

#define BASEDIR "/Users/drop/drop/cplus/uc/www";

//存放提取到的浏览器请求信息
typedef struct request_info{
         char filename[128];
         char protocol[16];
         char filetype[16];
         char method[12];
 } request_info_t;
//存放返回状态
typedef struct response {
    int status;
    int filetype;
} response_info_t;

//函数声明
request_info_t parse_requests(int); 
char *get_filename(char *str);
char *get_filetype(char *str);
char *get_method(char *str);
int get_response(int,request_info_t *);
void response404(int);
void response403(int);
static void get_code(response_info_t *,char *);
static void get_file_type(response_info_t *,request_info_t *);
static void get_response_head(int,response_info_t *);

#endif

项目结果展示

B9768331-B9EF-4BAA-B2E8-813A95C1D033.png

9B31023F-AB4E-4E24-98E0-BF81D48EBB47.png

AB7FA00F-4C36-40BB-A98E-11CF318B2F0D.png

Responses