Я хочу написать программу на C, чтобы сгенерировать запрос Get без использования каких-либо внешних библиотек. Возможно ли использование только библиотек C с использованием сокетов? Я собираюсь создать http-пакет (используя правильное форматирование) и отправить его на сервер. Это единственный возможный путь или есть лучший способ?HTTP get Request using C WITHOUT libCurl
ответ
Использование сокетов BSD или, если вы несколько ограничены, скажем, что у вас есть несколько RTOS, более простой стек TCP, например lwIP, вы можете сформировать запрос GET/POST.
Существует множество версий с открытым исходным кодом. См. «Happyhttp» в качестве образца (http://scumways.com/happyhttp/happyhttp.html). Я знаю, это C++, а не C, но единственное, что является «C++-зависимым», - это управление строкой/массивом, поэтому его легко переносить на чистый C.
Остерегайтесь, нет «пакетов», , поскольку HTTP обычно передается по TCP-соединению, поэтому технически в формате RFC имеется только поток символов. Поскольку HTTP-запросы обычно выполняются с помощью метода connect-send-disconnect, на самом деле можно назвать это «пакетом».
В принципе, как только у вас есть открытый сокет (sockfd) «все» вы должны сделать что-то вроде
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);
}
}
Спасибо! Это сделало то, что мне было нужно! – asudhak
@asudhak - Это отлично работает, пока этот код не должен запускаться в корпоративной рабочей среде, где доступ через Интернет осуществляется через прокси-сервер. Протокол для получения URL-адреса через HTTP-прокси немного отличается от прямого TCP. – selbie
@selbie - Конечно, ответы HTTP с кодом 300 (перенаправления) и прокси-файлы - это именно то, что затрудняет HTTP. Таким образом, tayloring libCurl, чтобы исключить разные криптосвязанные вещи, может быть способом, а не обработанным вручную HTTP-запросом. –
«Без каких-либо внешних библиотек», строго говоря, исключает Libc, так что вы бы нужно писать все syscalls самостоятельно. Я сомневаюсь, что вы имеете в виду это строго. Если вы не хотите ссылаться на другую библиотеку и не хотите копировать исходный код из другой библиотеки в ваше приложение, то ваш лучший подход к прямому взаимодействию с потоком TCP с помощью API сокетов.
Создание запроса HTTP и отправка его по адресу TCP socket connection легко, как читает ответ. Он анализирует ответ, который будет действительно сложным, особенно если вы намерены поддерживать достаточно значительную часть стандарта. Такие вещи, как страницы ошибок, переадресации, согласование контента и т. Д., Могут усложнить нашу жизнь, если вы разговариваете с произвольными веб-серверами. Если, с другой стороны, сервер, как известно, хорошо себя ведет, а простое сообщение об ошибке подходит для любого неожиданного ответа сервера, то это также достаточно просто.
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://stackoverflow.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);
}
Использование
Compile:
gcc -o wget wget.c
Получить http://example.com и выход на стандартный вывод:
./wget example.com
IP:
./wget 104.16.118.182
Эта команда зависании для большинства серверов, пока тайм-аут, и что ожидается:
- либо сервер или клиент должен закрыть соединение
- большинство серверов HTTP оставить соединение открывать до истечения времени ожидания, ожидая дальнейших запросов, напримерJavaScript, CSS и изображения после HTML страницы
- мы могли разобрать ответ, и закрываются, когда Content-Length байт для чтения, но мы не сделали для простоты
Испытано на Ubuntu 15.10.
стороне сервера Пример по адресу: Send and Receive a file in socket programming in Linux with C/C++ (GCC/G++)
GitHub вверх по течению: https://github.com/cirosantilli/cpp-cheat/blob/88d0c30681114647cce456c2e17aa2c5b31abcd0/posix/socket/wget.c
Код висит на 'read (socket_file_descriptor, buffer, BUFSIZ)'. – CroCo
@CroCo см. Комментарий к источнику: «второе чтение зависает в течение нескольких секунд. [...]». Любой сервер или клиент должен закрыть соединение. Мы не закрываем, поэтому ни один сервер. Это, вероятно, оптимизирует несколько HTTP-запросов, выполненных в одном соединении, что является обычным случаем (получить HTML, получить CSS, получить изображения). Клиенты обычно должны разбирать выходные данные и проверять, что ответ завершен и закрыт с помощью 'Content-Length:' в случае HTTP, но я не хотел анализировать HTTP в этом простом примере. –
- 1. RESTClient using HTTP Get Request
- 2. Libcurl C Http Post/Get
- 3. C - http post request with libcurl
- 4. libcurl C++ add http get parameters
- 5. c libcurl piping HTTP GET и PUT
- 6. Rubymotion: GET Request using JSON
- 7. http request get body
- 8. Андроид HTTP GET Request
- 9. Facebook GET Http Request
- 10. HTTP get request tcl
- 11. rails http get request
- 12. C++ LibCurl GET from Socket
- 13. XML HTTP GET Request Fails
- 14. http post request get SocketTimeoutException
- 15. Http Get Request возвращает html?
- 16. Python Get/POST http request
- 17. HTTP Get Request - Program Stops
- 18. Angular.js HTTP Get using $ routeParams
- 19. ajax call to webflow using get request
- 20. Http Get using Android HttpURLConnection
- 21. вызов http get using proxy
- 22. C# HTTP Request Parser
- 23. HTTP get with headers using RestTemplate
- 24. AngularJS $ http request using forEach внутри контроллера
- 25. Как отправить файл по адресу http get request C# code?
- 26. «Malformed JSON» from http get request
- 27. using shutil.copytree without copystat
- 28. Hang up on HTTP Get/Request
- 29. Добавить параметры для libcurl GET в C++
- 30. Кэш HTTP GET REQUEST в Python Sockets
Нет, вы должны научиться BSD-сокетов API, а затем вручную упаковать вместе все исходные данные. –