Я хотел бы создать очень простое приложение C, которое будет отправлять сообщения HTTP. Потребуется несколько параметров, которые будут использоваться для создания URL-адреса. Я просто хотел бы выполнить простой HTTP POST и получить ответ без использования curl (библиотеки не установлены и не будут установлены на машине, которую необходимо запустить).
Псевдокод:
Обработать 2 аргумента
Поместите аргументы в URL-адрес шаблона: http://api.somesite.com/apikey=ARG1&command=ARG2
Сделайте POST по сгенерированному URL
Потребляйте ответ
Мои поиски в Google и SO ничего не дали по этому поводу.
Ответы:
Сообщение состоит из заголовка и тела сообщения, разделенных пустой строкой. Пустая строка нужна ВСЕГДА, даже если нет тела сообщения. Заголовок начинается с команды и имеет дополнительные строки пар ключ-значение, разделенных двоеточием и пробелом. Если есть тело сообщения, это может быть что угодно.
Строки в заголовке и пустая строка в конце заголовка должны заканчиваться парой возврата 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 должна:
Вызовы send и receive не обязательно будут отправлять / получать ВСЕ данные, которые вы им даете - они вернут количество фактически отправленных / полученных байтов. Вы должны вызывать их в цикле и отправлять / получать оставшуюся часть сообщения.
Чего я не делал в этом примере, так это какой-либо реальной проверки ошибок - когда что-то не получается, я просто выхожу из программы. Дайте мне знать, работает ли это для вас:
#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 байт - это не очень большой ответ. Я выбрал это число наугад, предполагая, что ответ на ваш запрос будет коротким. Если он может быть большим, у вас есть два варианта:
Дополнительная информация для ответа на вопрос, заданный в комментариях:
Что, если вы хотите поместить данные POST в тело сообщения? Затем вам нужно включить заголовки Content-Type: и Content-Length :. Content-Length: это фактическая длина всего, что находится после пустой строки, отделяющей заголовок от тела.
Вот пример, который принимает следующие аргументы командной строки:
Итак, для исходного вопроса вы должны выполнить:
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; }
источник
\r\n
), но в этом коде используются простые переводы строки."POST /variableName=%s&value=%s HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 4\r\n\r\n\r\n"
Я хочу написать как name = reaz. Он отвечает на 400 плохих запросовОтвет Джерри великолепен. Однако он не обрабатывает большие ответы. Простое изменение, чтобы справиться с этим:
memset(response, 0, sizeof(response)); total = sizeof(response)-1; received = 0; do { printf("RESPONSE: %s\n", response); // HANDLE RESPONSE CHUCK HERE BY, FOR EXAMPLE, SAVING TO A FILE. memset(response, 0, sizeof(response)); bytes = recv(sockfd, response, 1024, 0); if (bytes < 0) printf("ERROR reading response from socket"); if (bytes == 0) break; received+=bytes; } while (1);
источник
bytes = recv(sockfd, response, 1023, 0)
После нескольких недель исследований. Я придумал следующий код. Я считаю, что это минимум, необходимый для безопасного подключения SSL к веб-серверу.
#include <stdio.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/bio.h> #define APIKEY "YOUR_API_KEY" #define HOST "YOUR_WEB_SERVER_URI" #define PORT "443" int main() { // // Initialize the variables // BIO* bio; SSL* ssl; SSL_CTX* ctx; // // Registers the SSL/TLS ciphers and digests. // // Basically start the security layer. // SSL_library_init(); // // Creates a new SSL_CTX object as a framework to establish TLS/SSL // or DTLS enabled connections // ctx = SSL_CTX_new(SSLv23_client_method()); // // -> Error check // if (ctx == NULL) { printf("Ctx is null\n"); } // // Creates a new BIO chain consisting of an SSL BIO // bio = BIO_new_ssl_connect(ctx); // // Use the variable from the beginning of the file to create a // string that contains the URL to the site that you want to connect // to while also specifying the port. // BIO_set_conn_hostname(bio, HOST ":" PORT); // // Attempts to connect the supplied BIO // if(BIO_do_connect(bio) <= 0) { printf("Failed connection\n"); return 1; } else { printf("Connected\n"); } // // The bare minimum to make a HTTP request. // char* write_buf = "POST / HTTP/1.1\r\n" "Host: " HOST "\r\n" "Authorization: Basic " APIKEY "\r\n" "Connection: close\r\n" "\r\n"; // // Attempts to write len bytes from buf to BIO // if(BIO_write(bio, write_buf, strlen(write_buf)) <= 0) { // // Handle failed writes here // if(!BIO_should_retry(bio)) { // Not worth implementing, but worth knowing. } // // -> Let us know about the failed writes // printf("Failed write\n"); } // // Variables used to read the response from the server // int size; char buf[1024]; // // Read the response message // for(;;) { // // Get chunks of the response 1023 at the time. // size = BIO_read(bio, buf, 1023); // // If no more data, then exit the loop // if(size <= 0) { break; } // // Terminate the string with a 0, to let know C when the string // ends. // buf[size] = 0; // // -> Print out the response // printf("%s", buf); } // // Clean after ourselves // BIO_free_all(bio); SSL_CTX_free(ctx); return 0; }
В приведенном выше коде подробно объясняется, как установить соединение TLS с удаленным сервером.
Важное примечание : этот код не проверяет, был ли открытый ключ подписан действительным органом. Это означает, что я не использую корневые сертификаты для проверки. Не забудьте выполнить эту проверку, иначе вы не узнаете, подключаете ли вы правильный веб-сайт.
Что касается самого запроса. Это не более чем написание HTTP-запроса вручную.
По этой ссылке вы также можете найти объяснение, как установить openSSL в вашей системе и как скомпилировать код, чтобы он использовал безопасную библиотеку .
источник
Ручка добавлена.
Добавлен заголовок Host.
Добавлена поддержка linux / windows, протестировано (XP, WIN7).
ПРЕДУПРЕЖДЕНИЕ: ОШИБКА: «ошибка сегментации», если в качестве аргумента нет хоста, пути или порта.
#include <stdio.h> /* printf, sprintf */ #include <stdlib.h> /* exit, atoi, malloc, free */ #include <unistd.h> /* read, write, close */ #include <string.h> /* memcpy, memset */ #ifdef __linux__ #include <sys/socket.h> /* socket, connect */ #include <netdb.h> /* struct hostent, gethostbyname */ #include <netinet/in.h> /* struct sockaddr_in, struct sockaddr */ #elif _WIN32 #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #pragma comment(lib,"ws2_32.lib") //Winsock Library #else #endif void error(const char *msg) { perror(msg); exit(0); } int main(int argc,char *argv[]) { int i; struct hostent *server; struct sockaddr_in serv_addr; int bytes, sent, received, total, message_size; char *message, response[4096]; int portno = atoi(argv[2])>0?atoi(argv[2]):80; char *host = strlen(argv[1])>0?argv[1]:"localhost"; char *path = strlen(argv[4])>0?argv[4]:"/"; 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")) { printf("Process 1\n"); message_size+=strlen("%s %s%s%s HTTP/1.0\r\nHost: %s\r\n"); /* method */ message_size+=strlen(argv[3]); /* path */ message_size+=strlen(path); /* 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 { printf("Process 2\n"); message_size+=strlen("%s %s HTTP/1.0\r\nHost: %s\r\n"); message_size+=strlen(argv[3]); /* method */ message_size+=strlen(path); /* 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 */ } printf("Allocating...\n"); /* 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\nHost: %s\r\n", strlen(argv[3])>0?argv[3]:"GET", /* method */ path, /* path */ strlen(argv[5])>0?"?":"", /* ? */ strlen(argv[5])>0?argv[5]:"",host); /* query string */ else sprintf(message,"%s %s HTTP/1.0\r\nHost: %s\r\n", strlen(argv[3])>0?argv[3]:"GET", /* method */ path,host); /* 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\nHost: %s\r\n", strlen(argv[3])>0?argv[3]:"POST", /* method */ path,host); /* 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",(int)strlen(argv[5])); strcat(message,"\r\n"); /* blank line */ if(argc>5) strcat(message,argv[5]); /* body */ } printf("Processed\n"); /* What are we going to send? */ printf("Request:\n%s\n",message); /* lookup the ip address */ total = strlen(message); /* create the socket */ #ifdef _WIN32 WSADATA wsa; SOCKET s; printf("\nInitialising Winsock..."); if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) { printf("Failed. Error Code : %d",WSAGetLastError()); return 1; } printf("Initialised.\n"); //Create a socket if((s = socket(AF_INET , SOCK_STREAM , 0 )) == INVALID_SOCKET) { printf("Could not create socket : %d" , WSAGetLastError()); } printf("Socket created.\n"); server = gethostbyname(host); serv_addr.sin_addr.s_addr = inet_addr(server->h_addr); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(portno); 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 to remote server if (connect(s , (struct sockaddr *)&serv_addr , sizeof(serv_addr)) < 0) { printf("connect failed with error code : %d" , WSAGetLastError()); return 1; } puts("Connected"); if( send(s , message , strlen(message) , 0) < 0) { printf("Send failed with error code : %d" , WSAGetLastError()); return 1; } puts("Data Send\n"); //Receive a reply from the server if((received = recv(s , response , 2000 , 0)) == SOCKET_ERROR) { printf("recv failed with error code : %d" , WSAGetLastError()); } puts("Reply received\n"); //Add a NULL terminating character to make it a proper string before printing response[received] = '\0'; puts(response); closesocket(s); WSACleanup(); #endif #ifdef __linux__ int sockfd; server = gethostbyname(host); if (server == NULL) error("ERROR, no such host"); sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) error("ERROR opening socket"); /* 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 */ 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; printf("Response: \n"); do { printf("%s", response); memset(response, 0, sizeof(response)); bytes = recv(sockfd, response, 1024, 0); if (bytes < 0) printf("ERROR reading response from socket"); if (bytes == 0) break; received+=bytes; } while (1); if (received == total) error("ERROR storing complete response from socket"); /* close the socket */ close(sockfd); #endif free(message); return 0; }
источник