2

私は自分のウェブサーバーを構築しています。今のところ、私の最小限のコードは次のとおりです。

#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>

#define SERVER_PORT 80

int main () {

    int nReqSocketId, nReqSize = 1024, nMainSocketId = socket(AF_INET, SOCK_STREAM, 0);
    char *sRequest = malloc(nReqSize);
    socklen_t nAddrLen;
    struct sockaddr_in oAddress;

    oAddress.sin_family = AF_INET;
    oAddress.sin_addr.s_addr = INADDR_ANY;
    oAddress.sin_port = htons(SERVER_PORT);

    if (nMainSocketId == 0) {
        fprintf(stderr, "Error during the creation of the socket\n");
        return 1;
    }

    if (bind(nMainSocketId, (struct sockaddr *) &oAddress, sizeof(oAddress))) {
        fprintf(stderr, "The port %d is busy\n", SERVER_PORT);
        close(nMainSocketId);
        return 1;
    }

    printf("HTTP server listening on port %d\n", SERVER_PORT);

    while (1) {

        if (listen(nMainSocketId, 10) < 0) {
            perror("server: listen");
            close(nMainSocketId);
            exit(1);
        }

        nReqSocketId = accept(nMainSocketId, (struct sockaddr *) &oAddress, &nAddrLen);

        if (nReqSocketId < 0) {
            perror("server: accept");
            close(nMainSocketId);
            exit(1);
        }

        recv(nReqSocketId, sRequest, nReqSize, 0);

        if (nReqSocketId > 0){
            printf("The Client is connected...\n\n%s\n", sRequest);
        }

        write(nReqSocketId, "HTTP/1.1 200 OK\n", 16);
        write(nReqSocketId, "Content-length: 50\n", 19);
        write(nReqSocketId, "Content-Type: text/html\n\n", 25);
        write(nReqSocketId, "<html><body><h1>Hello world!!!</h1></body></html>\n", 50);
        close(nReqSocketId);

    }

    printf("Goodbye!\n");

    close(nMainSocketId);

    return 0;

}

Webサーバーに「さようなら!」を印刷させる「ソフトクロージングメカニズム」を作成できますか?無限ループの後のフレーズ?たとえば、「q」の文字を入力すると…</p>

4

7 に答える 7

1

これらの書き込み関数をすべて削除して、単一の send() だけを使用しないのはなぜですか?

あなたがする必要があるのは、応答をバッファーに保存してから、バッファーを送信することだけです。

// Global
#define MAX 2048
char response[MAX]; // No need for char* 

// In Main
memset(response, 0, MAX); // **EDIT**
strcpy(response, "HTTP/1.1 200 OK\n");
strcat(response, "Content-length: 50\n");
strcat(response, "Content-Type: text/html\n\n");
strcat(response, "<html><body><h1>Hello world!!!</h1></body></html>\n");

// Now simply send the whole response in one go:
send(nReqSocketId, response, strlen(response), 0);

また、次のように単純にこれを非永続的な接続にすることもできます。

// Global
#define MAX 2048
char response[MAX];  // No need for char* 

// In Main
memset(response, 0, MAX); // **EDIT**
strcpy(response, "HTTP/1.1 200 OK\n");
strcat(response, "Content-Type: text/html\n\n");
strcat(response, "<html><body><h1>Hello world!!!</h1></body></html>\n");

// Now simply send the whole response in one go again:
send(nReqSocketId, response, strlen(response), 0);

// Shutdown the socket so it cannot write anymore:
shutdown(nReqSocketId,1);

// Then totally close it when you are ready:
close(nReqSocketId);

後者は、現在行っていることに適している可能性があります。とにかく、Webサーバーで複数の接続を維持していないためです。

サーバー側で接続を閉じると、クライアント (つまり、Web ブラウザー) はコンテンツの期待を停止することを認識し、ジョブを適切に終了します。

お役に立てれば。

乾杯!

PS-

もちろん、これはこのスレッドの最後の質問への回答であり、ソフト終了の部分ではありません。

memset(response, 0, MAX) にすることもお勧めします。これにより、応答するたびにきれいな状態になります。

于 2013-08-31T16:17:26.197 に答える
0

@PandaSobao

ファイル記述子と組み合わせて fprintf() を使用して write() を削除しました。 strlen() を必要としないため、これが最善の方法だと思います...以下のコードを見てください:

#include <stdio.h>
#include <netinet/in.h>
#include <signal.h>

#define REQUEST_SIZE 1024
#define SERVER_PORT 80

int bExiting, nMainSocketId;

void terminateServer () {
    if (bExiting) { return; }
    printf("\n\nTerminating server...\n");
    close(nMainSocketId);
    bExiting = 1;
}

int main () {

    int nRecvResult = -1, nReqSocketId = -1;
    char sRequest[REQUEST_SIZE];
    socklen_t nAddrLen;
    struct sockaddr_in oAddress;
    FILE *nRespFD;

    printf("\n  W E L C O M E!\n\nPress CTRL-C or CTRL-\\ to quit.\n");

    signal(SIGINT, terminateServer);
    signal(SIGQUIT, terminateServer);

    oAddress.sin_family = AF_INET;
    oAddress.sin_addr.s_addr = INADDR_ANY;
    oAddress.sin_port = htons(SERVER_PORT);

    nMainSocketId = socket(AF_INET, SOCK_STREAM, 0);

    if (nMainSocketId < 0) {
        perror("server: socket() failed");
        return 1;
    }

    if (bind(nMainSocketId, (struct sockaddr *) &oAddress, sizeof(oAddress))) {
        perror("server: bind() failed");
        terminateServer();
        return 1;
    }

    printf("HTTP server listening on port %d\n", SERVER_PORT);

    while (bExiting == 0) {

        if (listen(nMainSocketId, 10) < 0) {
            perror("server: listen() failed");
            terminateServer();
            return 1;
        }

        nReqSocketId = accept(nMainSocketId, (struct sockaddr *) &oAddress, &nAddrLen);

        if (bExiting) { break; }

        if (nReqSocketId < 0) {
            perror("server: accept() failed");
            terminateServer();
            return 1;
        }

        nRecvResult = recv(nReqSocketId, sRequest, REQUEST_SIZE, 0);

        if (nRecvResult < 0) {
            perror("server: recv() failed");
            terminateServer();
            return 1;
        }

        if (nRecvResult == 0) {
            printf("The client is disconnected. Waiting for another connection...\n");
            close(nReqSocketId);
            continue;
        }

        printf("The client is connected...\n\n%s\n", sRequest);

        nRespFD = fdopen(nReqSocketId, "a+");

        fprintf(nRespFD, "HTTP/1.1 200 OK\n");
        fprintf(nRespFD, "Content-length: 50\n");
        fprintf(nRespFD, "Content-Type: text/html\n\n");
        fprintf(nRespFD, "<html><body><h1>Hello world!!!</h1></body></html>\n");

        fclose(nRespFD);

        close(nReqSocketId);

    }

    printf("Goodbye!\n");

    return 0;

}

shutdown() と close() について、違いをよりよく説明できますか?

于 2013-08-31T17:05:14.943 に答える
0

さて、私の回答の 2 番目の部分では、Web サーバーを非永続的にする方法について説明します。Persistent-Connections は、HTTP 1.1 より前は Keep-Alive として知られていました。送信後に毎回応答ソケットを閉じていることがわかったので、Web サーバーを「非永続」にすることもできます。

これは、shutdown() のために "Content-Length: X" を送信する必要がないことを意味します。ソケットをシャットダウンするには、次の 3 つの方法があります。

"The constants SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, respectively..."

shutdown(SOCKET, 1) を実行することで、基本的にクライアント (ブラウザ) に FIN ack を送信し、ソケットが書き込みを完了したことを知らせます。したがって、「Content-Length: X」ヘッダー レスポンスを設定する必要はありません。

さて、これは shutdown() VS close() のものではありません。これは shutdown() AND close() です。ソケットを破棄するには、ソケットを close() する必要があります。シャットダウンはそれを行いません。

要約すると、複数の接続を追跡する必要がない場合は、ヘッダーから「Content-Length:」を削除することを検討してください。shutdown(SOCKET, 1) を「送信の終了」メカニズムとして使用し、後でソケットを破棄するために閉じます。

/---------------------------------------------------------------- -------------------------------------------------- -------------------------------/

文字列リテラルを送信するためだけに FILE* 全体を作成する限り、それだけの価値があるかどうかはわかりません。これをベンチに置いて、いくつかの結果を得ましたか? とにかく、パフォーマンスがあなたが探しているものであれば、 strcat() [もちろん strlen() を含む] と strlen() を一緒に回避する他の方法を知っています。そうだったとは知りませんでした。

PS- strcat() は最初はかなり高速ですが、大きなバッファーを連結するときにのみ、複雑さが増し始めます。SIMD を検索し、アーキテクチャに応じてこれらのタイプの関数がどのように最適化されるかを調べます。

于 2013-08-31T19:00:47.907 に答える