目录
仿照tinyhttpd项目写的,每个函数的实现可能有一些不一样,不过大体的函数功能是一样的
阅读代码的顺序为:
main()——>startup()——>accept_request()——>serve_file()——>execute_cig()。
流程图如下:
cgi模块执行原理
图例解释:定义两个管道input,output,
input:由父进程向子进程的输入管道
output:由子进程向父进程的输出管道
1、让子进程去执行cgi,父进程去做I/O操作。首先将子进程的标准输入重定向为input[0],将子进程的标准输出重定向为output[1],这样,子进程的输入来自父进程,子进程的输出是输出到父进程。通过管道进行父子进程的通信。
2、父进程通过recv接收来自客户端的表单输入,并且通过write传给子进程,子进程收到父进程的输入后,执行cgi脚本,执行完后,输出给父进程。父进程通过read读取到子进程的输出后,再通过send发送到客户端。
代码如下:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/wait.h>
#include <sys/stat.h>
#define SERVER_STRING "Server: tinyhttpd/1.0.0\r\n"
#define STDIN 0
#define STDOUT 1
#define STDERR 2
// 用来初始化服务器端socket
int startup(int *port);
// 用来输出错误情况
void error_die(const char *str);
// 用来处理请求
void *accept_request(void* client);
// 用来处理错误的请求方法
void unimplemented(int client_sock);
// 用来处理404错误
void not_found(int client_sock);
// 这个函数用来读取一行http请求,并把结尾的'\r\n'替换成'\n'
int get_line(int sock, char *buf, int size);
// 传递请求的页面给客户端
void server_file(int sock, char *path);
// 用来添加http响应头部
void headers(int sock);
// 用来发送响应实体
void cat(int client_sock, FILE *file);
// 用来执行cgi脚本
void execute_cgi(int client_sock, const char *path, const char *method, const char *query_string);
// 处理错误的请求
void bad_request(int sock);
// 处理内部错误的情况
void cannot_execute(int sock);
int main(void)
{
int server_sock, client_sock;
struct sockaddr_in client_addr;
socklen_t client_addr_size;
pthread_t newthread;
int port = 12345;
// 初始化服务器端socket
server_sock = startup(&port);
printf("httpd start port: %d...\n",port);
while(1)
{
// 建立一个新的客户端连接
client_sock = accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_size);
if(client_sock == -1)
error_die("accept");
// 每来一个客户端连接,就创建一个线程来处理请求
if(pthread_create(&newthread, NULL, accept_request, (void*)&client_sock) != 0)
error_die("pthread_create");
}
close(server_sock);
return 0;
}
int startup(int *port)
{
struct sockaddr_in serv_addr;
int serv_sock;
int on = 1;
//int port = 12345; // 端口
//char *ip = "172.22.29.7"; // ip
serv_sock = socket(PF_INET, SOCK_STREAM, 0);// 创建服务器端套接字
if(serv_sock == -1)
error_die("socket");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(*port);
// 复用套接字的端口
if((setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0)
error_die("setsockopt failed");
if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
error_die("bind");
if(*port == 0)
{
socklen_t serv_size = sizeof(serv_addr);
if(getsockname(serv_sock, (struct sockaddr*)&serv_addr, &serv_size) < 0)
error_die("getsockname");
*port = ntohs(serv_addr.sin_p