HTTP服务器(三)

xiaoxiao2021-03-01  33

下面实现处理动态页面的逻辑:

创建一对命名管道,fork创建子进程;让父子进程执行不同的任务

值得注意的是,由于管道数据流动是单向的,所以要创建一对;父进程将必要的信息通过管道传递给子进程,子进程将计算的结果通过管道传递给父进程。

int HanndlerCGI(int sock, Req* req) { err_code = 200; //1、创建一对匿名管道 int fd1[2], fd2[2]; pipe(fd1); pipe(fd2); int father_read = fd1[0]; int child_write = fd1[1]; int father_write = fd2[1]; int child_read = fd2[0]; //2、创建子进程 int ret = fork(); if(ret > 0) { // 1)父进程流程 close(child_write); close(child_read); HandlerCGIFather(sock, father_read, father_write, req, ret); } else if(ret == 0) { // 2)子进程流程 close(father_read); close(father_write); HandlerCGIChild(sock, child_read, child_write, req); } else { perror("fork"); err_code = 404; } return err_code; } 父进程将必要的信息传递给子进程,构造响应报文,将子进程计算生成的页面发送给客户端

传递信息给子进程:

如果是post方法,需要将body中的内容通过管道发送给子进程;

如果是get方法,query_string已经在之前获取到了,不需要作什么了。

构造响应报文:

由于将生成动态页面的任务交给了子进程,所以父进程只需要构造响应报文,将子进程通过管道传递给父进程的内容,发送给客户端。

void HandlerCGIFather(int sock, int father_read, int father_write, Req* req, int pid) { printf("in CGIFather\n"); //1.如果是post请求,将body中的数据写到管道中,由子进程完成动态页面的生成 if(strcmp(req->method, "POST") == 0) { char buf[1024] = {0}; read(sock, buf, req->content_length); write(father_write, buf, req->content_length); } //2.构造HTTP响应 const char* first_line = "HTTP/1.1 200 OK\n"; const char* blank_line = "\n"; send(sock, first_line, strlen(first_line), 0); send(sock, blank_line, strlen(blank_line), 0); //3.从管道中读取数据(子进程动态生成的页面),写到socket中 char c = '\0'; while(read(father_read, &c, 1) > 0) { write(sock, &c, 1); printf("%c\n", c); } printf("write to sock\n"); //4.进行进程等待,回收进程资源 waitpid(pid, NULL, 0); } 子进程通过程序替换,生成结果,发送给父进程

设置环境变量:

由于,进行程序替换后,就无法知道父进程发送给子进程的数据了,于是我们就要想办法让程序替换后的程序仍旧可以获得这些数;又因为我们发现环境变量是进行程序替换后,仍旧可以访问到的,所以,我们要做的第一步就是将必要的信息设置成环境变量。

把标准输入和标准输出重定向到管道上:

这样一来,我们实现的CGI程序往标准输出上写,就是往管道上写;于是只要是能够访问标准输入输出,以及环境变量的编程语言就都可以实现CGI程序了。

获取CGI程序的真实路径,进行程序替换,让替换后的程序完成具体的计算过程。

void HandlerCGIChild(int sock, int child_read, int child_write, Req* req) { printf("in CGIchild\n"); //1、设置环境变量 char method[1024], query_string[1024], content_length[1024]; sprintf(method, "REQUEST_METHOD=%s", req->method); putenv(method); printf("method putenv:%s\n", method); if(strcmp(req->method, "GET") == 0) { sprintf(query_string, "QUERY_STRING=%s", req->query_string); putenv(query_string); } else { printf("put content_length : %d\n", req->content_length); sprintf(content_length, "CONTENT_LENGTH=%d", req->content_length); putenv(content_length); } printf("before dup\n"); //2、把标准输入和输出重定向到管道上 dup2(child_read, 0); dup2(child_write, 1); //3、进行程序替换 // a)拼装url为一个完整的文件路径 char file_path[1024*4] = {0}; HandlerFilePath(req->url_path, file_path); // b)进行程序替换 int err = execl(file_path, file_path, NULL); //4、如果exec失败,进行错误处理 if(err < 0) { perror("execl error"); exit(0); } } 实现一个CGI程序(计算两个数相加)

#include <stdio.h> #include <string.h> #include <stdlib.h> int GetQueryString(char buf[]) { char* method = getenv("REQUEST_METHOD"); if(strcmp(method, "GET") == 0) { char* query_string = getenv("QUERY_STRING"); if(query_string == NULL) { fprintf(stderr, "get QUERY_STRING error\n"); } strcpy(buf, query_string); return 0; } else if(strcmp("POST", method) == 0) { fprintf(stderr, "in post get string\n"); char* content_len = getenv("CONTENT_LENGTH"); fprintf(stderr, "cont—len:%s\n", content_len); int content_length = atoi(content_len); fprintf(stderr, "cont—length:%d\n", content_length); int i = 0; char c = '\0'; while(i < content_length) { read(0, &c, 1); buf[i++] = c; } fprintf(stderr, "string:%s\n", buf); return 0; } return -1; } int main() { fprintf(stderr, "in math\n"); char query_string[1024*4] = {0}; int ret = GetQueryString(query_string); if(ret < 0) { fprintf(stderr, "GetQueryString error\n"); return 0; } fprintf(stderr, "query_string:%s\n", query_string); int a,b; sscanf(query_string, "a=%d&b=%d", &a, &b); fprintf(stderr, "after anylaze string\n"); int sum = a+b; printf("<h1>sum = %d</h1>", sum); fprintf(stderr, "%d+%d=%d\n", a, b, sum); }

 

转载请注明原文地址: https://www.6miu.com/read-4582204.html

最新回复(0)