2016-11-19 7 views
1

私は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; 
} 

子供が終了し、私は

を解答接続をクローズします私のコード内の論理間違いはありますか?

編集:

完全な最小コードを追加しました。 送信メッセージに「\ r \ n」を追加します。 私は)(メインに

close(accfd); 

を追加する場合は正常に動作しますが、私はそれが実際に問題(唯一の副作用溶液)

があるindex.htmをすることができないと思う。

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

必須の '\ n '終端文字はありますか? – Tibrogargan

+0

@Tibrogargan: '\ r \ n' – alk

+0

サーバは、不完全なデータセットをクライアントに送信する可能性が最も高いです。そのため、クライアントは欠落しているものを待ってから、何を得たかをレンダリングします。接続が途絶えた瞬間、クライアントは何も来ていないことを認識し、待機を停止し、何が得られたかを試します。バグは 'createAnswerMessage'にあります。 – alk

答えて

-1

親のファイル記述子を子にコピーしてfork()を呼び出すと、子プロセスのソケットに引き続きアクセスすることができます。ネットワークスタックは、少なくとも1つのファイルディスクリプタがソケットに対して開いている限り、ソケットを有効に保ちます。子プロセスでソケットを閉じても、親プロセスで開いています。

つまり、親にclose(accfd)へのコールを追加すると、問題が解決するはずです。

+0

私は、ソケットを開いたままにしておくことを強くお勧めします。クライアントは、潜在的なソケット接続が閉じられているかどうかに関係なく、受信したメッセージが完了した時点で結果をレンダリングすることが期待されます。サーバーは終了しますが、まだ... – alk

+0

はい、動作しますが、実際の問題は解決しないと思います。 – Linz

関連する問題