Ответ 1
Сообщение имеет часть заголовка и тело сообщения, разделенные пустой строкой. Пустая строка ВСЕГДА необходима, даже если тело сообщения отсутствует. Заголовок начинается с команды и содержит дополнительные строки пар ключ-значение, разделенные двоеточием и пробелом. Если есть тело сообщения, оно может быть чем угодно.
Строки в заголовке и пустая строка в конце заголовка должны заканчиваться парой возврата carraige и перевода строки (см. Стиль разрыва строки заголовка HTTP), поэтому в конце этих строк \r\n.
URL-адрес имеет вид http://host:port/path?query_string
Существует два основных способа отправки запроса на сайт:
-
GET: Строка запроса является необязательной, но, если она указана, должна быть достаточно короткой. Из-за этого заголовок может быть просто командой GET и ничем иным. Пример сообщения может быть:
GET /path?query_string HTTP/1.0\r\n \r\n
-
POST: то, что обычно находится в строке запроса, находится в теле сообщения. Из-за этого заголовок должен включать атрибуты Content-Type: и Content-Length:, а также команду POST. Пример сообщения может быть:
POST /path HTTP/1.0\r\n Content-Type: text/plain\r\n Content-Length: 12\r\n \r\n query_string
Итак, чтобы ответить на ваш вопрос: если URL, который вас интересует в POSTing, это http://api.somesite.com/apikey=ARG1&command=ARG2, тогда нет тела или строки запроса и, следовательно, нет причины для POST, потому что нечего вставлять в тело сообщения и, следовательно, нечего вставлять в Content-Type: и Content-Length:
Я полагаю, вы могли бы отправить, если вы действительно этого хотите. В этом случае ваше сообщение будет выглядеть так:
POST /apikey=ARG1&command=ARG2 HTTP/1.0\r\n
\r\n
Таким образом, чтобы отправить сообщение программе C необходимо:
- создать сокет
- искать IP-адрес
- открыть розетку
- отправить запрос
- ждать ответа
- закрыть розетку
Отправка и прием вызовов не обязательно будут отправлять/получать ВСЕ данные, которые вы им предоставляете - они будут возвращать количество байтов, фактически отправленных/полученных. Вам решать вызывать их в цикле и отправлять/получать оставшуюся часть сообщения.
В этом примере я не делал никакой реальной проверки ошибок - когда что-то не получается, я просто выхожу из программы. Дайте мне знать, если это работает для вас:
#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[])
{
/* first what are we going to send and where are we going to send it? */
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); }
/* fill in the parameters */
sprintf(message,message_fmt,argv[1],argv[2]);
printf("Request:\n%s\n",message);
/* create the socket */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("ERROR opening socket");
/* lookup the ip address */
server = gethostbyname(host);
if (server == NULL) error("ERROR, no such host");
/* fill in the structure */
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);
/* connect the socket */
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
/* send the request */
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);
/* receive the response */
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 the socket */
close(sockfd);
/* process response */
printf("Response:\n%s\n",response);
return 0;
}
Как и в другом ответе, 4096 байт не очень большой ответ. Я выбрал это число наугад, предполагая, что ответ на ваш запрос будет коротким. Если он может быть большим, у вас есть два варианта:
- прочитайте заголовок Content-Length: из ответа, а затем динамически выделите достаточно памяти для хранения всего ответа.
- напишите ответ в файл по мере поступления
Дополнительная информация для ответа на вопрос, заданный в комментариях:
Что если вы хотите поместить POST-данные в текст сообщения? Затем вам нужно включить заголовки Content-Type: и Content-Length :. Content-Length: фактическая длина всего, что находится после пустой строки, отделяющей заголовок от тела.
Вот пример, который принимает следующие аргументы командной строки:
- хозяин
- порт
- команда (GET или POST)
- путь (не включая данные запроса)
- данные запроса (помещаются в строку запроса для GET и в тело для POST)
- список заголовков (Content-Length: автоматически при использовании POST)
Итак, для первоначального вопроса вы должны запустить:
a.out api.somesite.com 80 GET "/apikey=ARG1&command=ARG2"
И для вопроса, заданного в комментариях, вы запустите:
a.out api.somesite.com 80 POST / "name=ARG1&value=ARG2" "Content-Type: application/x-www-form-urlencoded"
Вот код:
#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;
/* first where are we going to send it? */
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); }
/* How big is the message? */
message_size=0;
if(!strcmp(argv[3],"GET"))
{
message_size+=strlen("%s %s%s%s HTTP/1.0\r\n"); /* method */
message_size+=strlen(argv[3]); /* path */
message_size+=strlen(argv[4]); /* headers */
if(argc>5)
message_size+=strlen(argv[5]); /* query string */
for(i=6;i<argc;i++) /* headers */
message_size+=strlen(argv[i])+strlen("\r\n");
message_size+=strlen("\r\n"); /* blank line */
}
else
{
message_size+=strlen("%s %s HTTP/1.0\r\n");
message_size+=strlen(argv[3]); /* method */
message_size+=strlen(argv[4]); /* path */
for(i=6;i<argc;i++) /* headers */
message_size+=strlen(argv[i])+strlen("\r\n");
if(argc>5)
message_size+=strlen("Content-Length: %d\r\n")+10; /* content length */
message_size+=strlen("\r\n"); /* blank line */
if(argc>5)
message_size+=strlen(argv[5]); /* body */
}
/* allocate space for the message */
message=malloc(message_size);
/* fill in the parameters */
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", /* method */
strlen(argv[4])>0?argv[4]:"/", /* path */
strlen(argv[5])>0?"?":"", /* ? */
strlen(argv[5])>0?argv[5]:""); /* query string */
else
sprintf(message,"%s %s HTTP/1.0\r\n",
strlen(argv[3])>0?argv[3]:"GET", /* method */
strlen(argv[4])>0?argv[4]:"/"); /* path */
for(i=6;i<argc;i++) /* headers */
{strcat(message,argv[i]);strcat(message,"\r\n");}
strcat(message,"\r\n"); /* blank line */
}
else
{
sprintf(message,"%s %s HTTP/1.0\r\n",
strlen(argv[3])>0?argv[3]:"POST", /* method */
strlen(argv[4])>0?argv[4]:"/"); /* path */
for(i=6;i<argc;i++) /* headers */
{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"); /* blank line */
if(argc>5)
strcat(message,argv[5]); /* body */
}
/* What are we going to send? */
printf("Request:\n%s\n",message);
/* create the socket */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("ERROR opening socket");
/* lookup the ip address */
server = gethostbyname(host);
if (server == NULL) error("ERROR, no such host");
/* fill in the structure */
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);
/* connect the socket */
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
/* send the request */
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);
/* receive the response */
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 the socket */
close(sockfd);
/* process response */
printf("Response:\n%s\n",response);
free(message);
return 0;
}