0

サーバーの応答をブラウザーに返す前に、ポートで要求をリッスンし、その要求を別のサーバーに渡す、BSD ソケットを使用して単純なプロキシ サーバーを作成しようとしています。

以下のコードを使用して、ブラウザーから REST 要求を受け取ることができます。

void *buffer = malloc(512);
long length = 0;    

while (1) {
    void *tempBuffer = malloc(512);
    long response = recv(acceptedDescriptor, tempBuffer, 512, 0);

    if (response == 0 || response < 512) {
        free(tempBuffer);

        printf("Read %lu bytes\n", length);
        break;
    }

    memcpy(buffer + length, tempBuffer, response);
    free(tempBuffer);

    length += response;

    realloc(buffer, length + 512);
}

ただし、recv()接続がピア (この場合はブラウザー) によって閉じられた場合は 0 を返す必要がありますが、そうではありません。recv()接続が閉じられたかどうかを検出できる唯一の方法は、応答が から要求された最大量の 512 バイト未満であるかどうかを確認することです。一部のリクエストが不完全であるため、これは時々問題になります。

受信するデータがこれ以上ない場合は、recv()ブロックして戻りません。受け入れられた記述子を非ブロックに設定すると、読み取りループが永遠に続き、終了することはありません。

もし私が:

  1. リスニング ソケット記述子をノンブロッキングに設定すると、接続EAGAINしようとするとエラー (リソースが一時的に使用不可) が発生するaccept()

  2. 受け入れられたソケット記述子を非ブロックに設定し、recv()決して 0 を返さず、ループを永久に続行します

  3. accept()両方を非ブロッキングに設定すると、接続しようとすると「不正なファイル記述子」エラーが発生します

  4. どちらもノンブロッキングに設定しないでください。ループはrecv()決して返されないため、終了しません。

ソケット自体は次のように作成されますが、リクエストを検出できるため、初期化に問題はありません。

int globalDescriptor = -1;
struct sockaddr_in localServerAddress;
...
int initSocket() {
    globalDescriptor = socket(AF_INET, SOCK_STREAM, 0);

    if (globalDescriptor < 0) {
        perror("Socket Creation Error");
        return 0;
    }

    localServerAddress.sin_family = AF_INET;
    localServerAddress.sin_addr.s_addr = INADDR_ANY;
    localServerAddress.sin_port = htons(8374);

    memset(localServerAddress.sin_zero, 0, 8);

    int res = 0;
    setsockopt(globalDescriptor, SOL_SOCKET, SO_REUSEADDR, &res, sizeof(res));

    //fcntl(globalDescriptor, F_SETFL, O_NONBLOCK);

    return 1;
}
...
void startListening() {
    int bindResult = bind(globalDescriptor, (struct sockaddr *)&localServerAddress, sizeof(localServerAddress));

    if (bindResult < 0) {
        close(globalDescriptor);
        globalDescriptor = 0;

        perror("Socket Bind Error");
        exit(1);
    }

    listen(globalDescriptor, 1);

    struct sockaddr_in clientAddress;
    int clientAddressLength = sizeof(clientAddress);

    while (1) {
        memset(&clientAddress, 0, sizeof(clientAddress));
        clientAddressLength = sizeof(clientAddress);

        int acceptedDescriptor = accept(globalDescriptor, (struct sockaddr *)&clientAddress, (socklen_t *)&clientAddressLength);
        //fcntl(acceptedDescriptor, F_SETFL, O_NONBLOCK);

        if (acceptedDescriptor < 0) {
            perror("Incoming Connection Error");
            exit(1);
        }

        void *buffer = malloc(512);
        long length = 0;

        while (1) {
            void *tempBuffer = malloc(512);
            long response = recv(acceptedDescriptor, tempBuffer, 512, 0);

            if (response == 0) {
                free(tempBuffer);

                printf("Read %lu bytes\n", length);
                break;
            }

            memcpy(buffer + length, tempBuffer, response);
            free(tempBuffer);

            length += response;

            realloc(buffer, length + 512);
        }

        executeRequest(buffer, length, acceptedDescriptor);

        close(acceptedDescriptor);
        free(buffer);
    }
}
...

この関数は、 1 を返すstartListening()場合にのみ呼び出されます。initSocket()

int main(int argc, const char *argv[]) {
    if (initSocket() == 1) {
        startListening();
    }

    return 0;
}

私はおそらくここでばかげたことをしていますが、この問題とそれを修正する方法についてあなたが持っているかもしれない情報をいただければ幸いです.

4

1 に答える 1

0

REST リクエストHTTP メソッドであるため、適切に定義されたHTTP Message Lengthを持っているためrecv()、完全なメッセージが到着するまで待つ必要があります。

于 2016-10-06T06:59:34.643 に答える