0

私の友人が、プログラミングの演習について助けを求めました。彼は、チャンク エンコーディング用の単純な HTTP クライアントを作成しようとしています。1000 ミリ秒遅延した最後のチャンクは、select によってブロックされません (select もタイムアウトしません)。select とビジーループの recv() を削除しようとしましたが、最後のチャンクが到着しないようです (到着したとしても)。

コードはクリーンとはほど遠いもので、私がクリエイティブな選択と呼ぶものでいっぱいです。しかし、それは他のすべてのチャンクで機能するようです。遅延チャンクがこのことを壊す原因となる可能性があることに頭を悩ませることはできません。

何か案は?

#define _POSIX_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <time.h>

int main(int argc, char *argv[])
{
// check that parameters exist
if (argc != 4) {
    fprintf(stderr, "Wrong number of parameters\n");
    return 1;
}

// create addrinfo structure
struct addrinfo info;
memset(&info, 0, sizeof info); // set values to 0
info.ai_family = AF_UNSPEC; // unspecified IP version
info.ai_socktype = SOCK_STREAM; // TCP

struct addrinfo *results; // results structure list

// get info using command line parameters
int value = getaddrinfo(argv[1], argv[2], &info, &results);
// check return value
if (value != 0) {
    fprintf(stderr, "Failed to call getaddrinfo\n");
    return 1;
}

// create socket
int sock = socket(results->ai_family, results->ai_socktype,
                  results->ai_protocol);               
// check return value
if (sock == -1) {
    fprintf(stderr, "Failed to create socket\n");
    return 1;
} else {
    // connect to remote host
    int con = connect(sock, results->ai_addr, results->ai_addrlen);
    // check return value
    if (con == -1) {
        fprintf(stderr, "Failed to connect to host\n");
        freeaddrinfo(results);
        return 1;
    }
}

// free memory
freeaddrinfo(results);


// create request
char request[1000];

strcpy(request, "GET ");
strcat(request, argv[3]);
strcat(request, " HTTP/1.1\r\n");

strcat(request, "Host: ");
strcat(request, argv[1]);
strcat(request, "\r\n");

strcat(request, "Accept-Encoding: chunked\r\n\n\n");


// send request
int sent_bytes = send(sock, request, strlen(request) + 1, 0);

// check return value
if (sent_bytes == -1) {
    fprintf(stderr, "Failed to send to remote host\n");
    return 1;
}

printf("\n\nSending...\n\n%s", request);



// receive status
char status[13];
int bytes = recv(sock, status, sizeof status, 0);
printf("\nReceiving...\n\n");

// check return value
if (bytes == -1) {
    fprintf(stderr, "Failed to receive\n");
    return 1;
}
else if (bytes == 0) {
    fprintf(stderr, "Host closed the connection\n");
    return 1;
}

status[12] = '\0';
if (strstr(status, "HTTP/1.1 404")) {
      fprintf(stderr, "404 Not found.\n\n");
      return 1;
  } else if (!(strstr(status, "HTTP/1.1 200"))) {
    fprintf(stderr, "Unknown response.\n\n");
    return 1;
  }

// status should now be 200 OK



char next[2];
next[1] = '\0';

// check encoding
char* encoding = (char*)calloc(1, sizeof(char));
while (1) {
    char *temp = (char*)realloc(encoding, strlen(encoding) + 2);
    if (temp == NULL) {
        fprintf(stderr, "Failed to realloc\n");
        free(encoding);
        return 1;
    }
    encoding = temp;
    bytes = recv(sock, next, 1, 0);
    if (bytes == -1) {
        fprintf(stderr, "Failed to receive\n");
        free(encoding);
        return 1;
    }
    else if (bytes == 0) {
        fprintf(stderr, "Host closed the connection\n");
        free(encoding);
        return 1;
    }

    memcpy(encoding + strlen(encoding), next, 2);
    if (strstr(encoding, "transfer-encoding: chunked")) {
        free(encoding);
          break;
      }
      if (strstr(encoding, "\r\n\r\n")) {
        free(encoding);
          fprintf(stderr, "Unsupported encoding\n");
          return 1;
      }
}

// encoding should now be chunked


// read until message chunks begin
char* rest = (char*)calloc(1, sizeof(char));
while (1) {
    char *temp = (char*)realloc(rest, strlen(rest) + 2);
    if (temp == NULL) {
        fprintf(stderr, "Failed to realloc\n");
        free(rest);
        return 1;
    }
    rest = temp;
    bytes = recv(sock, next, 1, 0);
    if (bytes == -1) {
        fprintf(stderr, "Failed to receive\n");
        free(rest);
        return 1;
    }
    else if (bytes == 0) {
        fprintf(stderr, "Host closed the connection\n");
        free(rest);
        return 1;
    }

    memcpy(rest + strlen(rest), next, 2);
      if (strstr(rest, "\r\n\r\n")) {
        free(rest);
          break;
      }
}


// read chunks
char* response = (char*)calloc(1, sizeof(char));

while (1) {

    char chunksize_string[10];
    chunksize_string[0] = '\0';

    fd_set readfds;
    struct timeval tv;
    FD_ZERO(&readfds);
    FD_SET(sock, &readfds);
    tv.tv_sec = 10;
    tv.tv_usec = 500000;

    // read chunksize
    while (1) {
        int rv = select(sock + 1, &readfds, 0, 0, &tv);
        if (rv == -1) {
            fprintf(stderr, "Error in select\n");
            return 1;
        } else if (rv == 0) {
            fprintf(stderr, "Timeout occured\n");
            return 1;
        }

        bytes = recv(sock, next, 1, MSG_WAITALL);
        if (bytes == -1) {
            fprintf(stderr, "Failed to receive\n");
            free(response);
            return 1;
        }
        else if (bytes == 0) {
            fprintf(stderr, "Host closed the connection\n");
            free(response);
            return 1;
        }
        memcpy(chunksize_string + strlen(chunksize_string), next, 2);
        if (strstr(next, "\n")) {
            break;
        }
    }
    unsigned int chunksize;
    sscanf(chunksize_string, "%x\r\n", &chunksize);

    if (chunksize == 0) {
        break;
    }

    // read chunk
    char chunk[chunksize + 1];
    bytes = recv(sock, chunk, chunksize, 0);
    if (bytes == -1) {
        fprintf(stderr, "Failed to receive\n");
        free(response);
        return 1;
    }
    else if (bytes == 0) {
        fprintf(stderr, "Host closed the connection\n");
        free(response);
        return 1;
    }
    chunk[chunksize] = '\0';

    // reallocate space in response
    char *temp = (char*)realloc(response, strlen(response) + chunksize + 1);
    if (temp == NULL) {
        fprintf(stderr, "Failed to realloc\n");
        free(response);
        return 1;
    }
    response = temp;

    // add chunk to response
    memcpy(response + strlen(response), chunk, chunksize + 1);

    // read "\r\n"
    char t[2];
    bytes = recv(sock, t, 2, 0);
    if (bytes == -1) {
        fprintf(stderr, "Failed to receive\n");
        free(rest);
        return 1;
    }

}

printf("%s", response);

free(response);

// close the connection
close(sock);

return 0;
}

node.js サーバーは次のとおりです。

var restify = require('restify');
var server = restify.createServer();

server.get('/', function(request, response, next) {
    response.status(200);
    response.header('transfer-encoding', 'chunked');

    response.write('First line\n');
    response.write('Second line\n');
    response.write('Third line first part --');
    response.write('and a second part\n');

    setTimeout(function() {
        response.end('Delayed line\n');
    }, 1000);   

    return next();
});

server.listen(9999);
4

1 に答える 1

1

そのコードは確かに少し危険です。確実な修正を提案することはできませんが、これらは役立ちます:

1) 使用前にすべての変数と予約済みスペースを初期化します。例:char request[1000] = {0};などunsigned int chunksize = 0;

2) の使用を修正しますselect()。一度に 1 バイトを読み取る while() 内に select があるため、おそらく複数回ループします。select が fdset と timeout パラメータを変更することを考慮してください。これらは、ループの反復ごとに正しく設定する必要があります。

3) の使用を修正しますrecv()。チャンクを読み取る際、コードは recv が「chunksize」量のデータを返すと想定しています。少なくともそうであることを確認してください。ステータスを受け取る場合も同様です。

ps すべての警告を有効にしてコンパイルし、コンパイラ出力を読み取ります。提案されたすべてのエラーを修正します。

于 2013-06-17T12:23:44.863 に答える