C - popen() 和 fread() 导致 accept() 抛出错误

标签 c popen fread

我是 C 的新手,如果我真的很困惑,请见谅。

我试图让一个服务器进程 fork() 另一个进程通过 popen() 运行 C 程序。但是,一旦我尝试读取字节,我的主服务器进程中的 accept() 就会出现错误。这是我收到错误的地方。

有人可以帮忙吗

                pipe_pointer = popen(cgi_command, "r");

                if(pipe_pointer != NULL) {
                    while(!feof(pipe_pointer)) {
                        bytes_read = fread(buf, 1, sizeof(buf), pipe_pointer);

                        send(clientSock, buf, bytes_read, 0);

                    }
                }


                pclose(pipe_pointer);

这是完整的代码

/**
 * This method processes the first line of the HTTP request and stores it in the variable that the pointer
 * buffer points to. In this case that variable is the request variable in the request_handler method. Returns 0 if nothing was received.
 **/
int process_request(int clientSock, char *buffer) {
    #define EOL "\r\n"
    #define EOL_SIZE 2

    char *bufferPointer = buffer;
    int eol_matched = 0;

    while(recv(clientSock, bufferPointer, 1, 0) != 0) {
        if(*bufferPointer == EOL[eol_matched]) {
            ++eol_matched;

            if(eol_matched == EOL_SIZE) {
                *(bufferPointer+1-EOL_SIZE) = '\0';
                return(strlen(buffer));
            }
        }
        else {
            eol_matched = 0;
        }
        bufferPointer++;
    }
    return(0);
}

int validate_get_request(char *buffer) {
    char get_string[5] = "GET /";
    if(strncasecmp(buffer, get_string, 5) != 0) {
        return(-1);
    }
    else {
        return(1);
    }
}

/**
 *This method takes a valid request and a pointer to a empty path variable, then fills out the path variable with the path of the requested file. Also it stores the requested filename in the filename variable
 **/
void get_path(char *request, char *path, char *filename) {
    char buf[1024];
    int i = 0;
    char *pointer = request;
    pointer += 4;
    while(!ISspace(pointer[i]) && pointer[i] != '\0') {
        filename[i] = pointer[i];
        i++;
    }
    filename[i] = '\0';


    if(strcmp(filename, "/") == 0) {
        strcpy(filename, "/index.html");
    }

    if(strstr(filename, ".cgi") != NULL) {
        strcpy(path, CGIROOT);
    }
    else {
        strcpy(path, DOCROOT);
    }
    strcat(path, filename);
}


void send_ok_header(int clientSock, char *mime) {
    char buf[1024];
    //Create OK Header, then add the file

    strcpy(buf, "HTTP/1.0 200 OK\r\n");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, SERVER);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "Content-Type: ");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, mime);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "\r\n\r\n");
    send(clientSock, buf, strlen(buf), 0);
}

void send_not_found(int clientSock, char *mime) {
    char buf[1024];
    //Create not found Header, then add the file
    strcpy(buf, "HTTP/1.0 404 NOT FOUND\r\n");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, SERVER);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "Content-Type: ");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, mime);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "\r\n\r\n");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "404 Sorry this webpage cannot be found\n");
    send(clientSock, buf, strlen(buf), 0);
}

void send_unsupported(int clientSock, char *mime) {
    char buf[1024];
    //Create not found Header, then add the file
    strcpy(buf, "HTTP/1.0 415 UNSUPPORTED MEDIA TYPE\r\n");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, SERVER);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "Content-Type: ");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, mime);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "\r\n\r\n");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "415 Sorry this media type is not supported!!!\n");
    send(clientSock, buf, strlen(buf), 0);
}

void send_unimplemented(int clientSock, char *mime) {
    char buf[1024];
    //Create not found Header, then add the file
    strcpy(buf, "HTTP/1.0 501 UNIMPLEMENTED\r\n");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, SERVER);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "Content-Type: ");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, mime);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "\r\n\r\n");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "501 Sorry this server only implements valid GET request\n");
    send(clientSock, buf, strlen(buf), 0);
}



/**
 This method takes the filename and fills the variable mime with its appropriate MIME type. Returns -1 if there is an unrecognized filename
 **/
int get_mime_type(char *filename, char *mime, int *is_cgi) {
    //WHY DOES THIS START WITH SOME WEIRD INITIAL VALUE ON THE 2ND TIME A REQUEST IS MADE?????
    char extension[50] = "";
    int return_val = -1;
    char *pointer;
    int filename_len = sizeof(filename);
    int i = 0;

    pointer = strstr(filename, ".");
    if(pointer != NULL) {
        while(pointer[i] != '\0') {
            extension[i] = pointer[i];
            i++;
        }
    }


    if(strcasecmp(extension, ".html") == 0 || strcasecmp(extension, ".htm") == 0) {
        strcpy(mime, "text/html");
        return_val = 1;
    }

    else if(strcasecmp(extension, ".jpeg") == 0 || strcasecmp(extension, ".jpg") == 0) {
        strcpy(mime, "image/jpeg");
        return_val = 1;
    }

    else if(strcasecmp(extension, ".gif") == 0) {
        strcpy(mime, "image/gif");
        return_val = 1;
    }

    else if(strcasecmp(extension, ".txt") == 0) {
        strcpy(mime, "text/plain");
        return_val = 1;
    }

    //set the cgi variable to 1
    else if(strcasecmp(extension, ".cgi") == 0) {
        strcpy(mime, "text/html");
        *is_cgi = 1;
        return_val = 1;
    }



    return(return_val);
}



/**
 * This is the function that the handler thread executes to handle each request
 */
void request_handler(int clientSock) {
    FILE *pipe_pointer = NULL;
    int i = 0;
    int is_cgi = 0;
    char buf[1024], path[1024], request[1024], mime[20], filename[50], *query_string[50] = {NULL}, *query_pointer, cgi_command[1024], cgibuf[1024];
    long bytes_read;

    //Send the actual html file with header
    FILE *requestedfile = NULL;

    //Take a request and fill it in the request variable
    if(process_request(clientSock, request) != 0) {
        //If it is a get request
        if(validate_get_request(request) != -1) {


            query_pointer = strtok(request, " ?=&");
            strcpy(request, query_pointer);
            while(query_pointer != NULL) {
                query_string[i] = malloc(strlen(query_pointer) + 1);
                strcpy(query_string[i], query_pointer);
                query_pointer = strtok(NULL, " ?=&");
                i++;
            }
            query_string[i] = NULL;


            get_path(request, path, filename);

            access(path, R_OK);

            //if the file extension is unsupported throw a 415 code
            if(get_mime_type(filename, mime, &is_cgi) != 1) {
                send_unsupported(clientSock, mime);
            }
            else if(errno == ENOENT) { //filetype is recognized, but file cannot be found
                send_not_found(clientSock, mime);
            }
            else if(errno == EACCES) { //filetype and file found, but no read access to the file
                //fix this to a 403 error
                perror("no access");
            }
            else { //PUT THE CGI code somewhere in this else statement

                if(is_cgi == 1) {
                    i = 1;
                    //Create command to open cgi script
                    strcpy(cgi_command, "(cd ");
                    strcat(cgi_command, CGIROOT);
                    strcat(cgi_command, "; .");
                    strcat(cgi_command, filename);
                    strcat(cgi_command, " ");


                    while(query_string[2*i + 1] != NULL) {
                        strcat(cgi_command, query_string[2*i + 1]);
                        i++;
                    }
                    strcat(cgi_command, ")");

                    free(*query_string);





                    pipe_pointer = popen(cgi_command, "r");

                    if(pipe_pointer != NULL) {
                        while(!feof(pipe_pointer)) {

                            bytes_read = fread(cgibuf, 1, sizeof(cgibuf), pipe_pointer);

                            send(clientSock, cgibuf, bytes_read, 0);

                        }
                    }


                    pclose(pipe_pointer);
                }
                else {
                    //Open the file and start to send it
                    requestedfile = fopen(path, "rb");

                    //Send header
                    send_ok_header(clientSock, mime);

                    //Read the file by 1024 byte increments and send it
                    while(!feof(requestedfile)) {
                        bytes_read = fread(buf, 1, sizeof(buf), requestedfile);
                        send(clientSock, buf, bytes_read, 0);

                    }
                }
            }

        }
        else { //not a get request

            send_unimplemented(clientSock, mime);

        }
    }
    //Close the client socket when the request is fulfilled
    //thread will die after this
    close(clientSock);
}

int main(int argc, const char * argv[])
{
    int server_sock_desc;
    struct sockaddr_in name;

    int client_sock_desc;
    struct sockaddr_in client_name;
    socklen_t addr_size;

    pthread_t handler_thread;

    //connection setup
    server_sock_desc = socket(PF_INET, SOCK_STREAM, 0);
    if(server_sock_desc != -1) {
        memset(&name, 0, sizeof(name));
        name.sin_family = AF_INET;
        name.sin_port = htons(PORTNUMBER);
        name.sin_addr.s_addr = htonl(INADDR_ANY);
        int bind_result = bind(server_sock_desc, (struct sockaddr *) &name, sizeof(name));
        if(bind_result == 0) {
            if(listen(server_sock_desc, BACKLOG) < 0) {
                perror("listen failed");
            }

            addr_size = sizeof(client_name);

            //Server Loop will continue to run listening for clients connecting to the server
            while(1) {

                //new client attempting to connect to the server
                client_sock_desc = accept(server_sock_desc, (struct sockaddr *) &client_name, &addr_size);
                if(client_sock_desc == -1) {
                    perror("accept failed");
                }

                //connection starts here

                //create a thread for the new clients request to be handled
                if(pthread_create(&handler_thread, NULL, request_handler, client_sock_desc) != 0) {
                    perror("pthread_create failed");
                }
            }
        }
        else {
            perror("bind failed");
        }
    }
    else {
        perror("socket failed");
    }

}

fread() 执行后

程序执行立即跳转到这个循环并卡在第二行

while(recv(clientSock, bufferPointer, 1, 0) != 0) {
        if(*bufferPointer == EOL[eol_matched]) {
            ++eol_matched;

            if(eol_matched == EOL_SIZE) {
                *(bufferPointer+1-EOL_SIZE) = '\0';
                return(strlen(buffer));
            }
        }
        else {
            eol_matched = 0;
        }
        bufferPointer++;
    }

最佳答案

改变:

            if(client_sock_desc == -1) {
                perror("accept failed");
            }

到:

            if(client_sock_desc == -1) {
                if (errno == EINTR) {
                    continue;
                }
                perror("accept failed");
                exit(1);
            }

在中断的情况下返回到accept()。如果出现任何其他错误,您需要终止——您只是失败并启动了 request_handler 线程。

关于C - popen() 和 fread() 导致 accept() 抛出错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15172293/

相关文章:

c - 在 C 中分配二维数组

c - 我不知道它是如何工作的?

Python popen() 在要求时插入密码

C:从二进制文件中读取字符串

c - 用 C 解析 wtmp 日志

c++ - 从 utf 8 混合中分离繁体中文字符

c - 程序没有循环

python - 用 Python 的 Popen 替换 Bash 风格的进程

C: popen 从 stdout block 读取

Linux:在线程中进行像 fadvise 这样的系统调用的最具可扩展性的设计是什么?