HTTP get Request usando C SIN libCurl
sockets networking (3)
"Sin bibliotecas externas" estrictamente hablando excluiría libc también, por lo que tendría que escribir todas las llamadas de sistema usted mismo. Aunque dudo que lo diga así de estricto. Si no desea vincular a otra biblioteca, y no desea copiar el código fuente de otra biblioteca en su aplicación, su mejor enfoque es lidiar directamente con la secuencia TCP utilizando la API de socket.
Crear la solicitud HTTP y enviarla a través de una conexión de socket TCP es fácil, así como leer la respuesta. Está analizando la respuesta que será realmente complicada, particularmente si intentas respaldar una porción razonablemente grande del estándar. Cosas como páginas de error, redirecciones, negociación de contenido, etc. pueden complicarnos la vida si hablamos con servidores web arbitrarios. Si, por otro lado, se sabe que el servidor se comporta bien y que un mensaje de error simple está bien para cualquier respuesta inesperada del servidor, entonces eso también es razonablemente simple.
Quiero escribir un programa en C para generar una Solicitud de obtención sin utilizar ninguna biblioteca externa. ¿Es posible usar solo bibliotecas C, usando sockets? Estoy pensando en crear un paquete http (usando el formato adecuado) y enviarlo al servidor. ¿Es esta la única forma posible o hay una mejor manera?
Usando sockets BSD o, si estás algo limitado, digamos que tienes algunos RTOS, una pila TCP más simple, como lwIP, puedes formar la solicitud GET / POST.
Hay una serie de implementaciones de código abierto. Vea el "happyhttp" como muestra ( http://scumways.com/happyhttp/happyhttp.html ). Lo sé, es C ++, no C, pero lo único que es "dependiente de C ++" es que hay una gestión de cadena / matriz, por lo que es fácilmente portado a C. puro
Tenga cuidado, no hay "paquetes", ya que HTTP generalmente se transfiere a través de la conexión TCP, por lo que técnicamente solo hay una secuencia de símbolos en formato RFC. Como las solicitudes http generalmente se realizan en una forma de conexión-envío-desconexión, en realidad se podría llamar a esto un "paquete".
Básicamente, una vez que tienes un socket abierto (sockfd) "todo" lo que tienes que hacer es algo así como
char sendline[MAXLINE + 1], recvline[MAXLINE + 1];
char* ptr;
size_t n;
/// Form request
snprintf(sendline, MAXSUB,
"GET %s HTTP/1.0/r/n" // POST or GET, both tested and works. Both HTTP 1.0 HTTP 1.1 works, but sometimes
"Host: %s/r/n" // but sometimes HTTP 1.0 works better in localhost type
"Content-type: application/x-www-form-urlencoded/r/n"
"Content-length: %d/r/n/r/n"
"%s/r/n", page, host, (unsigned int)strlen(poststr), poststr);
/// Write the request
if (write(sockfd, sendline, strlen(sendline))>= 0)
{
/// Read the response
while ((n = read(sockfd, recvline, MAXLINE)) > 0)
{
recvline[n] = ''/0'';
if(fputs(recvline,stdout) == EOF) { cout << ("fputs erros"); }
/// Remove the trailing chars
ptr = strstr(recvline, "/r/n/r/n");
// check len for OutResponse here ?
snprintf(OutResponse, MAXRESPONSE,"%s", ptr);
}
}
Ejemplo ejecutable mínimo de POSIX 7
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
int main(int argc, char** argv) {
char buffer[BUFSIZ];
enum CONSTEXPR { MAX_REQUEST_LEN = 1024};
char request[MAX_REQUEST_LEN];
char request_template[] = "GET / HTTP/1.1/r/nHost: %s/r/n/r/n";
struct protoent *protoent;
char *hostname = "example.com";
in_addr_t in_addr;
int request_len;
int socket_file_descriptor;
ssize_t nbytes_total, nbytes_last;
struct hostent *hostent;
struct sockaddr_in sockaddr_in;
unsigned short server_port = 80;
if (argc > 1)
hostname = argv[1];
if (argc > 2)
server_port = strtoul(argv[2], NULL, 10);
request_len = snprintf(request, MAX_REQUEST_LEN, request_template, hostname);
if (request_len >= MAX_REQUEST_LEN) {
fprintf(stderr, "request length large: %d/n", request_len);
exit(EXIT_FAILURE);
}
/* Build the socket. */
protoent = getprotobyname("tcp");
if (protoent == NULL) {
perror("getprotobyname");
exit(EXIT_FAILURE);
}
socket_file_descriptor = socket(AF_INET, SOCK_STREAM, protoent->p_proto);
if (socket_file_descriptor == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
/* Build the address. */
hostent = gethostbyname(hostname);
if (hostent == NULL) {
fprintf(stderr, "error: gethostbyname(/"%s/")/n", hostname);
exit(EXIT_FAILURE);
}
in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list)));
if (in_addr == (in_addr_t)-1) {
fprintf(stderr, "error: inet_addr(/"%s/")/n", *(hostent->h_addr_list));
exit(EXIT_FAILURE);
}
sockaddr_in.sin_addr.s_addr = in_addr;
sockaddr_in.sin_family = AF_INET;
sockaddr_in.sin_port = htons(server_port);
/* Actually connect. */
if (connect(socket_file_descriptor, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) {
perror("connect");
exit(EXIT_FAILURE);
}
/* Send HTTP request. */
nbytes_total = 0;
while (nbytes_total < request_len) {
nbytes_last = write(socket_file_descriptor, request + nbytes_total, request_len - nbytes_total);
if (nbytes_last == -1) {
perror("write");
exit(EXIT_FAILURE);
}
nbytes_total += nbytes_last;
}
/* Read the response.
*
* The second read hangs for a few seconds, until the server times out.
*
* Either server or client has to close the connection.
*
* We are not doing it, and neither is the server, likely to make serving the page faster
* to allow fetching HTML, CSS, Javascript and images in a single connection.
*
* The solution is to parse Content-Length to see if the HTTP response is over,
* and close it then.
*
* http://.com/a/25586633/895245 says that if Content-Length
* is not sent, the server can just close to determine length.
**/
fprintf(stderr, "debug: before first read/n");
while ((nbytes_total = read(socket_file_descriptor, buffer, BUFSIZ)) > 0) {
fprintf(stderr, "debug: after a read/n");
write(STDOUT_FILENO, buffer, nbytes_total);
}
fprintf(stderr, "debug: after last read/n");
if (nbytes_total == -1) {
perror("read");
exit(EXIT_FAILURE);
}
close(socket_file_descriptor);
exit(EXIT_SUCCESS);
}
Uso
Compilar:
gcc -o wget wget.c
Obtenga http://example.com y envíe a stdout:
./wget example.com
IP:
./wget 104.16.118.182
Este comando se bloquea para la mayoría de los servidores hasta el tiempo de espera, y eso se espera:
- el servidor o el cliente deben cerrar la conexión
- la mayoría de los servidores HTTP dejan la conexión abierta hasta que se agota el tiempo de espera de solicitudes adicionales, por ejemplo, JavaScript, CSS e imágenes después de una página HTML
- podríamos analizar la respuesta y cerrar cuando se leen los bytes de Content-Length, pero no lo hicimos por simplicidad
Probado en Ubuntu 15.10.
Un ejemplo del lado del servidor en: Enviar y recibir un archivo en la programación de socket en Linux con C / C ++ (GCC / G ++)
GitHub upstream: https://github.com/cirosantilli/cpp-cheat/blob/88d0c30681114647cce456c2e17aa2c5b31abcd0/posix/socket/wget.c