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(). 您在 while() 中选择了一次读取 1 个字节的位置,因此它可能会循环多次。考虑到 select 会修改 fdset 和 timeout 参数。这些需要为循环的每次迭代正确设置。

3) 修复recv(). 在读取块时,代码假定 recv 返回“块大小”的数据量。至少检查一下。接收状态时同样适用。

ps 在启用所有警告的情况下编译并读取编译器输出。修复所有建议的错误。

于 2013-06-17T12:23:44.863 回答