Page 247
The do loop at the end of maintask i.e.:
char buffer[2048]; do {
n = read(sockfd, buffer, 2048); buffer[n] = 0; printf("\ndata received %d\n\n", n); printf("%s\n", buffer); } while (n > 0);
Is too simple to put the data fragments back together correctly in the buffer.
The correct code is:
int len = 1024 * 2;
char buffer[len];
int m = 0;
do
{
int n = read(sockfd, buffer + m, len - m -1);
if (n < 0)
break;
m = m + n;
buffer[m] = 0;
printf("\ndata received %d\n\n", n);
// printf("%s\n", buffer);
} while (true);
Notice that we now wait for the HTTP server to timeout and disconnect from the socket connection – this can take tens of seconds.
Of course, we can do much better than this simple example. For one thing each socket operation needs to be checked for errors.
Page 250
A better way to set the number of sockets in select is to use sockfd+1.
Change:
n = select(5, &rfds, NULL, NULL, &tv);
To
n = select(sockfd+1, &rfds, NULL, NULL, &tv);
Page 251
Refactor the code into a funtion. Replace the code by:
void socketReadTimeout(int sockfd, char buffer[], int len, int timeoutsec)
{
int m = 0; fd_set rfds; struct timeval tv; do
{
FD_ZERO(&rfds); FD_SET(sockfd, &rfds); tv.tv_sec = timeoutsec; tv.tv_usec = 0; int s = select(sockfd + 1, &rfds, NULL, NULL, &tv); if (s < 0) {
printf("read error %X\n", errno); break; }
if (s == 0) { printf("read timeout\n"); break; } int n = read(sockfd, buffer + m, len - m - 1); if (n >= 0) { m = m + n; buffer[m] = 0; printf("\ndata received %d\n\n", n); } if (n < 0)
{
if (errno == ENOTCONN)
{
printf("socket closed by server\n");
break;
}
printf("read error %X\n", errno);
break;
}
vTaskDelay(2);
} while (true);
}
For example:
int len = 1024 * 2; char buffer[len]; socketReadTimeout(sockfd, buffer, len,2); printf("Final buffer\n\n%s\n", buffer);
Page 252
It has been pointed out the the example hard codes the server name into the headers which stops it being used more generally.
The corrections from page 252 to 255 are concerned with implementing this improvement.
To correct this we need to add a field to the Page struct and make use of it:
Change:
typedef struct { char *url; char *buffer; int len; bool done; } Page;
the url is the page we want to retrieve, buffer[len] is the character array that will store the result and done is true when the data is in buffer. While not necessary, an initialization function makes the struct easier to use:
Page initPage(char url[], char buffer[], int len) { Page page; page.url = url; page.buffer = buffer; page.len=len; page.done = false; return page; }
To:
typedef struct { char *url; char *path; char *buffer; int len; bool done; } Page;
the url is the server we want to connect to, path gives the page to load, buffer[len] is the character array that will store the result and done is true when the data is in buffer. While not necessary, an initialization function makes the struct easier to use:
Page initPage(char url[], char path[], char buffer[], int len) { Page page; page.url = url; page.path = path; page.buffer = buffer; page.len = len; page.done = false; return page; }
Notice we now have a path field which is used to specify the page and the URL now specified just the server.
Page 254
We now have to modify the rest of the code to use the new path field in the construction of the headers:
Change:
char header[] = "GET /index.html HTTP/1.1\r\n Host: example.com\r\n\r\n "; int n = write(sockfd, header, strlen(header)); if (n < 0) {
printf("no write %X\n", errno); printf("close socket %d\n", sockfd); close(sockfd); vTaskDelete(NULL); } else printf("data sent socket %d %d\n", sockfd, n);
to read:
char header[100];
sprintf(header, "GET %s HTTP/1.1\r\nHost:%s\r\n\r\n ", page->path, page->url); int n = write(sockfd, header, strlen(header)); if (n < 0) { printf("no write %X\n", errno); printf("close socket %d\n", sockfd); close(sockfd); vTaskDelete(NULL); } else printf("data sent socket %d %d\n", sockfd, n);
Page 254
To allow the program to handle reads split across packets using the function:
fd_set rfds; struct timeval tv; do { FD_ZERO(&rfds); FD_SET(sockfd, &rfds); tv.tv_sec = 2; tv.tv_usec = 0; n = select(5, &rfds, NULL, NULL, &tv); if (n < 0) {
printf("read error %X\n", errno); break; }
if (n == 0) { printf("read timeout\n"); break; } n = read(sockfd, page->buffer, page->len); if (n < 0) { printf("read error %X\n", errno); break; }
page->buffer[n] = 0; printf("\ndata recieved on socket %d %d\n\n", sockfd, n);
} while (true);
to read:
socketReadTimeout(sockfd, page->buffer,page-> len, 2); printf("close socket %d\n", sockfd); close(sockfd); page->done = true; vTaskDelete(NULL); }