32384


Cで書いたHTTPサーバーがレスポンスを返しません  
何が原因なのでしょうか?

原因として考えられるのは、`recv`は何もデータが来なければブロックするようなので、そこで動作が止まってしまい、サーバーがデータを送信できなくなっているのでしょうか?

[code]

    #include <stdio.h>
    #include <stdint.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <inttypes.h>
    #include <string.h>
    #include <errno.h>
    
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    int main() {
        int64_t MSG_BUF_SIZE = 4000;
    
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock == -1) {
            perror("Socket creation error");
        }
        struct sockaddr_in server_addr;
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(3211);
        server_addr.sin_addr.s_addr = INADDR_ANY;
    
        if (bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
            perror("Bind Error");
            return EXIT_FAILURE;
        }
    
        if (listen(sock, 11) != 0) {
            perror("Listen Error");
            return EXIT_FAILURE;
        }
    
        char send_message_buf[4096];
        memset(send_message_buf, 0, sizeof(send_message_buf));
        snprintf(send_message_buf, sizeof(send_message_buf), "HTTP/1.0 200 OK\r\n"
         "Content-Length: 20\r\n"
         "Content-Type: text/html\r\n"
         "\r\n"
         "HELLO\r\n");
    
        while (true) {
            struct sockaddr_in client;
            memset(&client, 0, sizeof(client));
            int accept_sock = accept(sock, (struct sockaddr *)&client, (socklen_t *)&client);
            if (accept_sock == -1) {
                perror("Accept Error");
                return EXIT_FAILURE;
            }
            char read_buf[3000];
            size_t read_length = 0;
            char *msg_buf = calloc(MSG_BUF_SIZE, sizeof(char));
            ssize_t r;
            memset(read_buf, 0, sizeof(read_buf));
            while ((r = recv(accept_sock, read_buf, sizeof(read_buf), 0)) > 0) { // ここでブロックしている?
                printf("r = %zd\n", r);
                if (read_length + r > MSG_BUF_SIZE - 1) {
                    MSG_BUF_SIZE *= 2;
                    msg_buf = realloc(msg_buf, (MSG_BUF_SIZE + 1) * sizeof(char));
                }
                memmove(msg_buf + read_length, read_buf, r);
                read_length += r;
            }
    
            if (r == 0) {
                msg_buf = strcat(msg_buf, "");
                printf("%s\n", msg_buf);
            }
            else if(r == -1) {
                perror("recv Error");
                return EXIT_FAILURE;
            }
    
            free(msg_buf);
            if (send(accept_sock, send_message_buf, strlen(send_message_buf), 0) == -1) {
                perror("Send Error");
            }
        }
    }
    
[/code]


CC BY-SA 3.0
2017-02-04T12:24:23.567
32382
2017-02-04T14:15:32.487
5246
question
c http
HTTPサーバがレスポンスを返さない


HTTPはバージョン1.1よりPersistent
Connectionsがデフォルトとなっています。そこでHTTP/1.1に対応したWebブラウザーではブラウザー側からコネクションを切断することはありません。そのためサーバー側の

>
[code]

>     while ((r = recv(accept_sock, read_buf, sizeof(read_buf), 0)) > 0) { //
> ここでブロックしている?
>  
[/code]

が完了することはありません。読み取れたところまでで中断し、リクエストヘッダーを解析する処理に移る必要があります。


CC BY-SA 3.0
2017-02-04T12:48:32.797
32384
2017-02-04T12:48:32.797
4236
32382
answer


質問の主題の「何が原因なのでしょうか?」については、ご自身の推定およびsayuriさんの回答にある通りなので、ここではHTTPリクエストの終了を判定するコードを示しておきます。

[code]

        while ((r = recv(accept_sock, read_buf, sizeof(read_buf), 0)) > 0) {
            printf("r = %zd\n", r);
            if (read_length + r > MSG_BUF_SIZE - 1) {
                MSG_BUF_SIZE *= 2;
                msg_buf = realloc(msg_buf, (MSG_BUF_SIZE + 1) * sizeof(char));
            }
            memmove(msg_buf + read_length, read_buf, r);
            read_length += r;
    
            //受信した内容を解析する必要がある
            char *end_of_header = strstr(msg_buf, "\r\n\r\n");
            //ヘッダー部を全部受信しているか
            if( end_of_header != NULL ) {
                //Content-Length:の位置を求める
                char *content_length_position = strcasestr(msg_buf, "Content-Length:");
                if( content_length_position == NULL || content_length_position > end_of_header ) {
                    //Content-Length:フィールドがヘッダ中に存在しないなら受信を終了する
                    break;
                }
                int content_length = atoi(content_length_position+strlen("Content-Length:"));
                printf("content_length=%d\n", content_length);
                if(strlen(end_of_header+strlen("\r\n\r\n")) >= content_length) {
                    //ボディ部の長さがすでにContent-Lengthで示されたバイト数を超えている時も受信を終了する
                    break;
                }
            }
        }
    
[/code]

ヘッダー部を全部受信していたら、その中から`Content-
Length:`を探し出し、そこに示された長さ分のボディ部を受信していたら受信待ちループから脱出すると言うことをしています。実行効率は気にせず、その他あれこれ細かいことを無視した簡易処理ですが、大雑把にどんなチェックが必要かの理解の補助にはなると思います。

ちなみにレスポンスの方にも正しい`Content-Length:`を指定してやらないと、ブラウザの方がタイムアウトまでレスポンスを待ち続けることになります。

[code]

    snprintf(send_message_buf, sizeof(send_message_buf), "HTTP/1.0 200 OK\r\n"
             "Content-Length: 7\r\n" //<- レスポンスボディになる"HELLO\r\n"の長さにしないといけない
             "Content-Type: text/html\r\n"
             "\r\n"
             "HELLO\r\n");
    
[/code]


CC BY-SA 3.0
2017-02-04T13:38:47.290
32387
2017-02-04T14:15:32.487
2017-02-04T14:15:32.487
13972
13972
32382
answer