服务器 - 浏览器仅在服务器终止后显示站点

时间:2016-11-19 00:00:54

标签: c sockets http tcp

我正在尝试用fork()编写一个小型HTTP服务器。当我通过firefox连接到它时,它不会显示该页面,直到我终止服务器。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/socket.h>
#include <signal.h>

#define LISTEN_MAX 5
#define CONT_MAX 10000
#define PORT 8081
#define MAX_FILE 2
#define S_SHORT 50
#define MAX_CONTENT 1000
#define MAX_HEADER 200
const size_t BUF_LEN = 1024; //was 128
const size_t REQUEST_LEN=1024;
char file_names[MAX_FILE][S_SHORT];
FILE *file_deskriptors[MAX_FILE];
int file_sizes[MAX_FILE];
char file_contents[MAX_FILE][MAX_CONTENT];

// Something unexpected happened. Report error and terminate.
void sysErr( char *msg, int exitCode ) {
    fprintf( stderr, "%s\n\t%s\n", msg, strerror( errno ) );
    exit( exitCode );
}

// get_line was borrowed from Tiny HTTPd under GPLv2
// https://sourceforge.net/projects/tinyhttpd/?source=typ_redirect
int get_line(int sock, char *buf, int size) {
    int i = 0;
    char c = '\0';
    int n;

    while ((i < size - 1) && (c != '\n'))
    {
        n = recv(sock, &c, 1, 0);
        /* DEBUG printf("%02X\n", c); */
        if (n > 0)
        {
            if (c == '\r')
            {
                n = recv(sock, &c, 1, MSG_PEEK);
                /* DEBUG printf("%02X\n", c); */

                if ((n > 0) && (c == '\n'))
                    recv(sock, &c, 1, 0);
                else
                    c = '\n';
            }
            buf[i] = c;
            i++;
        }
        else
            c = '\n';
    }
    buf[i] = '\0';
    return(i);
}

void copyHeaderToBuffer(char *tx_buff, int *status) {
    switch(*status) {
        case 200: strcpy(tx_buff,"HTTP/1.0 200 OK\r\nContent­type: text/html\r\n\r\n"); break;

    }
    return;
}

void answer(int *accfd, char *request ) {
    int file_size, file_index, status, sent_bytes;
    file_size, file_index, status = 0;
    char method[S_SHORT], ressource[S_SHORT], proto[S_SHORT];
    char tx_buff[MAX_CONTENT+MAX_HEADER];

    //rehash query
    splitRequest(request, method, ressource, proto);

    //check for <GET>
    checkMethod(method);

    //search the file and get index
    getFileIndexByName(ressource, &file_index);

    file_size = file_sizes[file_index];

    status = getFileStatus(&file_index);

    createAnswerMessage(tx_buff, &status, &file_index);

    //send the answer
    if ( (sent_bytes= write( accfd, tx_buff, strlen(tx_buff))) ==  -1 ) {
    sysErr( "[-] Client Fault: SEND", -4 );
    }

    return;
}

void createAnswerMessage(char *tx_buff, int *status, int *file_index) {
    copyHeaderToBuffer(tx_buff, status);
    strcat(tx_buff,file_contents[*file_index]);
    strcat(tx_buff,"\r\n");
    return;
}

int getFileStatus(int *file_index) {
    return 200;
}

void splitRequest(char *request, char *method, char *ressource, char *proto) {
    char *temp;
    if ((temp = strtok(request, " ")) != NULL) {
        strcpy(method, temp);
    }

    if ((temp = strtok(NULL, " ")) != NULL) {
        strcpy(ressource, temp);
    }

    if ((temp = strtok(NULL, " ")) != NULL) {
        strcpy(proto, temp);
    }
    //remove leading "/" from ressource
    cleanRessource(ressource);
    return;
}

void cleanRessource(char *ressource) {
    if (*ressource == '/') {
    printf("\nstr_len_ressource: %i",strlen(ressource));
    for ( int i=0; i < strlen(ressource); i++ ) {
        ressource[i]=ressource[i+1];
        }
    }
    return;
}

void checkMethod(char *method){
    if (strcmp(method, "GET") ) {
        printf("\n[-] Error: Method \"%s\" not known .",method);
        exit(0);
    }
    printf("\nincheckMethod method = %s",method);
    return;
}

void getFileIndexByName (char *ressource, int *file_index) {
    for (int i=0; i<MAX_FILE; i++) {
        if ( !strcmp(ressource, file_names[i]) ) {
            *file_index = i;
            return;
        }
    }
    printf("\[-] Error: File \"%s\" not known.",ressource);
    exit(0);
}

void filesInit () {
    memset(file_names, '\0', sizeof(file_names));
    memset(file_contents, '\0', sizeof(file_contents));
    //define your files here:
    strcpy(file_names[0],"index.htm");
    for (int i=0; i<MAX_FILE; i++) {
        //choose only existing files
        if (file_names[i][0]!='\0') {
            //open file
            file_deskriptors[i] = fopen(file_names[i],"r");
            //get file size
            fseek(file_deskriptors[i], 0, SEEK_END);
            file_sizes[i] = ftell(file_deskriptors[i]);
            //read the file content to file_contents
            fseek(file_deskriptors[i], 0, SEEK_SET);
            fread(file_contents[i], 1, CONT_MAX, file_deskriptors[i]);
        }
    }
    return;
}

void filesClose() {
    return;
}

int main(int argc, char **argv)
{
    //kill childs if recieving SIGCHLD
    signal(SIGCHLD,SIG_IGN);
    int connfd, accfd;

    struct sockaddr_in server_addr, client_addr;
    socklen_t sockaddr_len = sizeof(struct sockaddr_in);

    //initial the available files on server
    filesInit();
    // create socket
    if ( ( connfd = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0) {
        sysErr( "Server Fault : SOCKET", -1 );
    }
    // Set params so that we receive IPv4 packets from anyone on the specified port
    memset( &server_addr, 0, sockaddr_len );
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_family      = AF_INET;
    server_addr.sin_port        = htons( PORT );

    //bind socket to port
    if ( bind( connfd, (struct sockaddr *) &server_addr, sockaddr_len ) < 0 ) {
        sysErr( "\n[-] Server Fault : BIND", -2 );
    }else{printf("[+] SERVER ONLINE");}

    //let server listen for incoming connections
    if ( listen( connfd, LISTEN_MAX) < 0 ) {
            sysErr( "[-] Server Fault : LISTEN", -3 );
    }

    //main loop for accepting clients
    while ( true ) {
        pid_t pid;
        //connecting specific client
        if ( (accfd=accept( connfd, (struct sockaddr *) &client_addr, &sockaddr_len )) < 0 ) {
        sysErr( "[-] Server Fault : ACCEPT", -4 );
        }
        //fork & answer
        else {
        printf("\n[+] CLIENT CONNECTED\n");
        switch ( pid = fork() ) {
        case -1:    {
                    printf("\n[-] Error while fork()");
                    return EXIT_FAILURE;
                    }
        case 0:     {
                    int req_line_len=1;         //length of request line
                    int first_line_on = 1;      //set first line parameter
                    char req_line[S_SHORT];     //current read line
                    char first_line[S_SHORT];   //save first line

                    memset(req_line, 0, S_SHORT);
                    memset(first_line, 0, S_SHORT);

                    printf("\n[+] HTTP REQUEST on accfd: %i",accfd);
                    //reading line by line from socket
                    while((req_line_len > 0) && strcmp("\n", req_line)){
                        req_line_len = get_line( accfd, req_line, S_SHORT-1);
                        //get first line and save it
                        if (first_line_on) { first_line_on = 0; strcpy(first_line,req_line); }
                        if((req_line_len > 0) && strcmp("\n", req_line)) printf("%s",req_line);
                    }
                    //answering to client
                    answer(accfd, first_line);
                    //close connection
                    if (!close(accfd)) {printf("\n[+] CONNECTION CLOSED");}
                    exit(0);
                    break;
                    }
        default:    {
                    //main process
                    break;
                    }
        }
    }
}
//close listening socket
close( connfd );
//close server files
filesClose();
return 0;
}

孩子被终止了,我得到了答案CONNECTION CLOSED

我的代码中是否存在逻辑错误?

编辑:

我添加了完整的最小代码。 &#34; \ r \ n&#34;我添加到发送消息。 如果我添加

,它可以正常工作
close(accfd);

到main(),但我认为它实际上不是问题(只有副作用解决方案)

index.htm可以是:

<html><body><b>index</b><br>C is a interesting language!</body></html>

1 个答案:

答案 0 :(得分:-1)

当您调用fork()时,父项的文件描述符被复制到子项中,这就是您仍然可以访问子进程中的套接字的原因。只要至少有一个文件描述符对套接字开放,网络堆栈就会使套接字保持活动状态。您在子进程中关闭套接字但仍在父进程中打开它。

换句话说,在父母中添加对close(accfd)的调用可以解决您的问题。