私の友人が、プログラミングの演習について助けを求めました。彼は、チャンク エンコーディング用の単純な 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);