Un mensaje tiene una parte de encabezado y un cuerpo del mensaje separados por una línea en blanco. La línea en blanco SIEMPRE es necesaria incluso si no hay cuerpo del mensaje. El encabezado comienza con un comando y tiene líneas adicionales de pares clave-valor separados por dos puntos y un espacio. Si hay un cuerpo de mensaje, puede ser cualquier cosa que desee.
Las líneas en el encabezado y la línea en blanco al final del encabezado deben terminar con un retorno carraige y un par de salto de línea (ver estilo de salto de línea del encabezado HTTP ), por eso esas líneas tienen \ r \ n al final.
Una URL tiene la forma de http://host:port/path?query_string
Hay dos formas principales de enviar una solicitud a un sitio web:
GET: la cadena de consulta es opcional pero, si se especifica, debe ser razonablemente corta. Debido a esto, el encabezado podría ser solo el comando GET y nada más. Un mensaje de muestra podría ser:
GET /path?query_string HTTP/1.0\r\n
\r\n
POST: Lo que normalmente estaría en la cadena de consulta está en el cuerpo del mensaje. Debido a esto, el encabezado debe incluir los atributos Content-Type: y Content-Length: así como el comando POST. Un mensaje de muestra podría ser:
POST /path HTTP/1.0\r\n
Content-Type: text/plain\r\n
Content-Length: 12\r\n
\r\n
query_string
Entonces, para responder a su pregunta: si la URL en la que está interesado en PUBLICAR es http://api.somesite.com/apikey=ARG1&command=ARG2, entonces no hay cuerpo o cadena de consulta y, en consecuencia, no hay razón para PUBLICAR porque no no es nada para poner en el cuerpo del mensaje y, por lo tanto, nada para poner en Content-Type: y Content-Length:
Supongo que podrías PUBLICAR si realmente quisieras. En ese caso, su mensaje se vería así:
POST /apikey=ARG1&command=ARG2 HTTP/1.0\r\n
\r\n
Entonces, para enviar el mensaje, el programa C necesita:
- crear un enchufe
- buscar la dirección IP
- abre el enchufe
- enviar la solicitud
- espera la respuesta
- cerrar el enchufe
Las llamadas de envío y recepción no enviarán / recibirán necesariamente TODOS los datos que les proporcione; devolverán la cantidad de bytes realmente enviados / recibidos. Depende de usted llamarlos en un bucle y enviar / recibir el resto del mensaje.
Lo que no hice en esta muestra es ningún tipo de verificación de errores reales: cuando algo falla, simplemente salgo del programa. Déjame saber si te funciona:
#include <stdio.h> /* printf, sprintf */
#include <stdlib.h> /* exit */
#include <unistd.h> /* read, write, close */
#include <string.h> /* memcpy, memset */
#include <sys/socket.h> /* socket, connect */
#include <netinet/in.h> /* struct sockaddr_in, struct sockaddr */
#include <netdb.h> /* struct hostent, gethostbyname */
void error(const char *msg) { perror(msg); exit(0); }
int main(int argc,char *argv[])
{
int portno = 80;
char *host = "api.somesite.com";
char *message_fmt = "POST /apikey=%s&command=%s HTTP/1.0\r\n\r\n";
struct hostent *server;
struct sockaddr_in serv_addr;
int sockfd, bytes, sent, received, total;
char message[1024],response[4096];
if (argc < 3) { puts("Parameters: <apikey> <command>"); exit(0); }
sprintf(message,message_fmt,argv[1],argv[2]);
printf("Request:\n%s\n",message);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("ERROR opening socket");
server = gethostbyname(host);
if (server == NULL) error("ERROR, no such host");
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portno);
memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
total = strlen(message);
sent = 0;
do {
bytes = write(sockfd,message+sent,total-sent);
if (bytes < 0)
error("ERROR writing message to socket");
if (bytes == 0)
break;
sent+=bytes;
} while (sent < total);
memset(response,0,sizeof(response));
total = sizeof(response)-1;
received = 0;
do {
bytes = read(sockfd,response+received,total-received);
if (bytes < 0)
error("ERROR reading response from socket");
if (bytes == 0)
break;
received+=bytes;
} while (received < total);
if (received == total)
error("ERROR storing complete response from socket");
close(sockfd);
printf("Response:\n%s\n",response);
return 0;
}
Como se señaló en la otra respuesta, 4096 bytes no es una respuesta muy grande. Elegí ese número al azar asumiendo que la respuesta a su solicitud sería breve. Si puede ser grande, tiene dos opciones:
- lea el encabezado Content-Length: de la respuesta y luego asigne dinámicamente suficiente memoria para contener la respuesta completa.
- escribir la respuesta en un archivo a medida que llegan las piezas
Información adicional para responder a la pregunta formulada en los comentarios:
¿Qué sucede si desea PUBLICAR datos en el cuerpo del mensaje? Luego, debe incluir los encabezados Content-Type: y Content-Length :. La longitud del contenido: es la longitud real de todo después de la línea en blanco que separa el encabezado del cuerpo.
Aquí hay una muestra que toma los siguientes argumentos de línea de comando:
- anfitrión
- Puerto
- comando (GET o POST)
- ruta (sin incluir los datos de la consulta)
- datos de consulta (poner en la cadena de consulta para GET y en el cuerpo para POST)
- lista de encabezados (Content-Length: es automático si se usa POST)
Entonces, para la pregunta original, ejecutaría:
a.out api.somesite.com 80 GET "/apikey=ARG1&command=ARG2"
Y para la pregunta formulada en los comentarios, ejecutaría:
a.out api.somesite.com 80 POST / "name=ARG1&value=ARG2" "Content-Type: application/x-www-form-urlencoded"
Aquí está el código:
#include <stdio.h> /* printf, sprintf */
#include <stdlib.h> /* exit, atoi, malloc, free */
#include <unistd.h> /* read, write, close */
#include <string.h> /* memcpy, memset */
#include <sys/socket.h> /* socket, connect */
#include <netinet/in.h> /* struct sockaddr_in, struct sockaddr */
#include <netdb.h> /* struct hostent, gethostbyname */
void error(const char *msg) { perror(msg); exit(0); }
int main(int argc,char *argv[])
{
int i;
int portno = atoi(argv[2])>0?atoi(argv[2]):80;
char *host = strlen(argv[1])>0?argv[1]:"localhost";
struct hostent *server;
struct sockaddr_in serv_addr;
int sockfd, bytes, sent, received, total, message_size;
char *message, response[4096];
if (argc < 5) { puts("Parameters: <host> <port> <method> <path> [<data> [<headers>]]"); exit(0); }
message_size=0;
if(!strcmp(argv[3],"GET"))
{
message_size+=strlen("%s %s%s%s HTTP/1.0\r\n");
message_size+=strlen(argv[3]);
message_size+=strlen(argv[4]);
if(argc>5)
message_size+=strlen(argv[5]);
for(i=6;i<argc;i++)
message_size+=strlen(argv[i])+strlen("\r\n");
message_size+=strlen("\r\n");
}
else
{
message_size+=strlen("%s %s HTTP/1.0\r\n");
message_size+=strlen(argv[3]);
message_size+=strlen(argv[4]);
for(i=6;i<argc;i++)
message_size+=strlen(argv[i])+strlen("\r\n");
if(argc>5)
message_size+=strlen("Content-Length: %d\r\n")+10;
message_size+=strlen("\r\n");
if(argc>5)
message_size+=strlen(argv[5]);
}
message=malloc(message_size);
if(!strcmp(argv[3],"GET"))
{
if(argc>5)
sprintf(message,"%s %s%s%s HTTP/1.0\r\n",
strlen(argv[3])>0?argv[3]:"GET",
strlen(argv[4])>0?argv[4]:"/",
strlen(argv[5])>0?"?":"",
strlen(argv[5])>0?argv[5]:"");
else
sprintf(message,"%s %s HTTP/1.0\r\n",
strlen(argv[3])>0?argv[3]:"GET",
strlen(argv[4])>0?argv[4]:"/");
for(i=6;i<argc;i++)
{strcat(message,argv[i]);strcat(message,"\r\n");}
strcat(message,"\r\n");
}
else
{
sprintf(message,"%s %s HTTP/1.0\r\n",
strlen(argv[3])>0?argv[3]:"POST",
strlen(argv[4])>0?argv[4]:"/");
for(i=6;i<argc;i++)
{strcat(message,argv[i]);strcat(message,"\r\n");}
if(argc>5)
sprintf(message+strlen(message),"Content-Length: %d\r\n",strlen(argv[5]));
strcat(message,"\r\n");
if(argc>5)
strcat(message,argv[5]);
}
printf("Request:\n%s\n",message);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("ERROR opening socket");
server = gethostbyname(host);
if (server == NULL) error("ERROR, no such host");
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portno);
memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
total = strlen(message);
sent = 0;
do {
bytes = write(sockfd,message+sent,total-sent);
if (bytes < 0)
error("ERROR writing message to socket");
if (bytes == 0)
break;
sent+=bytes;
} while (sent < total);
memset(response,0,sizeof(response));
total = sizeof(response)-1;
received = 0;
do {
bytes = read(sockfd,response+received,total-received);
if (bytes < 0)
error("ERROR reading response from socket");
if (bytes == 0)
break;
received+=bytes;
} while (received < total);
if (received == total)
error("ERROR storing complete response from socket");
close(sockfd);
printf("Response:\n%s\n",response);
free(message);
return 0;
}