3

C で単純な Web サーバーを作成しようとしています。今のところ、接続を受信し、メッセージを完全に受信できます。ただし、HTTP/1.0 プロトコルに従って、「\r\n\r\n」シーケンスが検出されたときにクライアントに情報を送り返すことができるようにしたいと考えています。ただし、Telnet を使用してサーバーをテストする場合、「\r\n\r\n」と入力すると、クライアントで「^]」を押すまでサーバーは何もしません。これを Apache に対してテストしましたが、Apache にはこの問題はありません。そのため、Apache の動作を模倣する方法に関する情報を期待していました。私のコードは以下に追加されていますが、私はどこまでも完了しておらず、多くのエラーチェックを実装していないことに注意してください. ありがとう!

main(){
        int sock_fd = 0;
        int client_fd = 0;
        struct sockaddr_in socket_struct;
        /*Creates the socket*/
        if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
                fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
                exit(EXIT_FAILURE);
        }/*Ends the socket creation*/

        /*Populates the socket address structure*/
        socket_struct.sin_family = AF_INET;
        socket_struct.sin_addr.s_addr=INADDR_ANY;
        socket_struct.sin_port =htons(port);

        if (bind(sock_fd, (struct sockaddr*) &socket_struct, sizeof(socket_struct)) < 0)
        {
                fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
                exit(EXIT_FAILURE);
        }//Ends the binding.

        if (listen(sock_fd, 5) <0)
        {
                fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
                exit(EXIT_FAILURE);
        }//Ends the listening function

        if ( (client_fd = accept(sock_fd, NULL, NULL)) <0)
        {
                fprintf(stderr, "%s: %s\n", getprogname(), strerror(errno));
                exit(EXIT_FAILURE);
        }//Ends the accepting.
        while ( (size = read(client_fd, msg, 1024)) > 0)
        {
                //size = recv(client_fd, msg, 1024, MSG_PEEK|MSG_WAITALL);
                if ( (msg[size-4] == 13) && (msg[size-3] == 10)&&(msg[size-2] == 13) && (msg[size-1] == 10) )
                {
                        char* buffer = (char *)malloc(sizeof("The msg was: ")+ sizeof(msg));
                        sprintf(buffer, "The msg was: %s", msg);
                        send(client_fd, buffer, sizeof("The msg was: ")+ sizeof(msg), MSG_OOB);
                }

        }//ends the while loop for receiving data
        close(client_fd);
}
4

1 に答える 1

2

まず、次の行は機能しません。

while ( (size = read(client_fd, msg, 1024)) > 0)

これにより、メッセージのチャンクが受信され、受信したチャンクごとに最後のチャンクが上書きされます。次のようにします。

char msg[1024];
size_t pos = 0;
while (1) {
    assert(pos < sizeof(msg));
    ssize_t amt = read(client_fd, msg + pos, sizeof(msg) - pos);
    if (amt < 0)
        err(1, "read failed");
    if (amt == 0)
        break; // EOF
    pos += amt;
    char *e = strstr(msg, "\r\n\r\n");
    if (e) {
        size_t msglen = e - msg;
        /* Handle a complete message here */
    }
}

このようにして、メッセージのチャンクを受信すると、それらはバッファに書き込まれます。シーケンスを取得したら"\r\n\r\n"、メッセージ全体を扱うことができます。

重要な教訓: TCP では、パケットの境界とメッセージの境界が完全に異なる可能性があり、取得するサイズはさらに異なるread可能性があります。返されるデータの量を見るのではなく、データ自体を見てメッセージの終わりを見つける必要がありますread()(0 を返すときに通知される EOF を除くread())。

脚注:帯域外データが、あなたが思っているようなことをするとは思いません。

send(client_fd, buffer, sizeof("The msg was: ")+ sizeof(msg), MSG_OOB);

帯域外データは TCP の風変わりな機能であり、最新のプロトコルではほぼ確実に回避する必要があります。その実装は、 1 バイトの帯域外データのみを安全に送信できるという点で、プラットフォームごとに異なります。

これは、Telnet プロトコル (および Telnet 上に構築された FTP) によっていくつかの目的で使用されますが、HTTP では目的がありません。

于 2012-10-27T20:09:27.330 に答える