Exemplo C simples de fazer um HTTP POST e consumir a resposta


92

Eu gostaria de criar um aplicativo C muito simples que faz uma postagem HTTP. Levará alguns parâmetros e os usará para construir um URL. Gostaria apenas de fazer um HTTP POST simples e obter a resposta sem o uso de curl (as bibliotecas não são e não serão instaladas na máquina que precisa ser executada).

Pseudo-código:

  1. Args de processo 2

  2. Coloque args no URL do modelo: http://api.somesite.com/apikey=ARG1&command=ARG2

  3. Faça POST no URL gerado

  4. Consumir resposta

Minhas pesquisas no Google e no SO não renderam nada sobre esse assunto.


2
Você usa algum tipo de estrutura de rede? Qual sistema operacional você usa?
— cnicutar

Será apenas uma caixa básica do Fedora ou Cent. As estruturas de rede são os usuais sys / socket, netdb, arpa / inet. Só não libcurl.
— kmarks2

1
Não é libcurl. Você está disposto a ir com qualquer outra biblioteca ou tem que ser totalmente POSIX.
— cnicutar

Todos POSIX, infelizmente. Deve ser totalmente independente em qualquer sistema.
— kmarks2

2
Tenho uma amostra que fiz para você, mas não entendo por que você está usando o POST se não há corpo para a mensagem. Se todos os parâmetros estão na string de consulta, por que você não deseja fazer um GET?
— Jerry Jeremiah

Respostas:


196

Uma mensagem possui uma parte do cabeçalho e um corpo da mensagem separados por uma linha em branco. A linha em branco é SEMPRE necessária, mesmo se não houver corpo da mensagem. O cabeçalho começa com um comando e tem linhas adicionais de pares de valores-chave separados por dois pontos e um espaço. Se houver um corpo de mensagem, pode ser qualquer coisa que você quiser.

As linhas no cabeçalho e a linha em branco no final do cabeçalho devem terminar com um retorno carraige e um par de alimentação de linha (consulte o estilo de quebra de linha do cabeçalho HTTP ), então é por isso que essas linhas têm \ r \ n no final.

Um URL tem a forma de http://host:port/path?query_string

Existem duas maneiras principais de enviar uma solicitação a um site:

  • GET: A string de consulta é opcional, mas, se especificada, deve ser razoavelmente curta. Por causa disso, o cabeçalho pode ser apenas o comando GET e nada mais. Um exemplo de mensagem pode ser:

  • POST: o que normalmente estaria na string de consulta está no corpo da mensagem. Por causa disso, o cabeçalho precisa incluir os atributos Content-Type: e Content-Length: bem como o comando POST. Um exemplo de mensagem pode ser:

Portanto, para responder à sua pergunta: se o URL para o qual você está interessado em POSTAR for http://api.somesite.com/apikey=ARG1&command=ARG2, então não há corpo ou string de consulta e, conseqüentemente, não há razão para POSTAR porque há não é nada para colocar no corpo da mensagem e, portanto, nada para colocar no Content-Type: e Content-Length:

Eu acho que você poderia POSTAR se realmente quisesse. Nesse caso, sua mensagem seria assim:

Portanto, para enviar a mensagem, o programa C precisa:

  • criar um soquete
  • procure o endereço IP
  • abra a tomada
  • envie o pedido
  • espere pela resposta
  • feche o soquete

As chamadas de envio e recebimento não necessariamente enviarão / receberão TODOS os dados que você fornecer - elas retornarão o número de bytes realmente enviados / recebidos. Depende de você chamá-los em um loop e enviar / receber o restante da mensagem.

O que não fiz neste exemplo é qualquer tipo de verificação de erro real - quando algo falha, eu simplesmente saio do programa. Deixe-me saber se funciona para você:

Como a outra resposta apontada, 4096 bytes não é uma resposta muito grande. Escolhi esse número ao acaso, supondo que a resposta à sua solicitação seria curta. Se puder ser grande, você tem duas opções:

  • leia o cabeçalho Content-Length: da resposta e, em seguida, aloque dinamicamente memória suficiente para conter a resposta inteira.
  • escreva a resposta em um arquivo à medida que as peças chegam

Informações adicionais para responder à pergunta feita nos comentários:

E se você quiser POSTAR dados no corpo da mensagem? Em seguida, você precisa incluir os cabeçalhos Content-Type: e Content-Length :. O Content-Length: é o comprimento real de tudo após a linha em branco que separa o cabeçalho do corpo.

Aqui está um exemplo que leva os seguintes argumentos de linha de comando:

  • hospedeiro
  • porta
  • comando (GET ou POST)
  • caminho (não incluindo os dados da consulta)
  • dados de consulta (colocados na string de consulta para GET e no corpo para POST)
  • lista de cabeçalhos (Content-Length: é automático se estiver usando POST)

Portanto, para a pergunta original, você executaria:

E para a pergunta feita nos comentários que você faria:

Aqui está o código:


Quais argumentos devem ser transmitidos quando chamados?
— Santiago Martí Olbrich

Você precisa passar algo que será usado como apikey como primeiro parâmetro e algo no segundo parâmetro que será usado como comando. Se você deseja usar uma string de consulta completamente diferente, você precisa alterar a string de formato, o número de parâmetros e a mensagem de uso.
— Jerry Jeremiah

2
Este código emite uma solicitação HTTP malformada. HTTP especifica que as linhas de solicitação devem ser encerradas por pares de retorno de carro / alimentação de linha ( \r\n), mas este código usa alimentações de linha simples.
— John Bollinger

@JohnBollinger Isso é verdade. Obrigado por apontar isso. Esperançosamente, a resposta editada é melhor.
— Jerry Jeremiah

O que há de errado com esta mensagem de postagem? "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"Eu quero postar como name = reaz. Ele responde 400 Bad Request
— Reaz Murshed

12

A resposta de Jerry é ótima. No entanto, ele não lida com grandes respostas. Uma mudança simples para lidar com isso:


3
Você poderia apenas aumentar o array de resposta no meu exemplo. Presumi que ele estava apenas recebendo json e não estava baixando um arquivo enorme, mas é claro que até json pode ter megabytes, dependendo da consulta ...
— Jerry Jeremiah

1
Sou iniciante em C e sua resposta pode estar correta. Mas você pode acrescentar uma explicação à sua resposta?
— boop

2
Este é realmente apenas um comentário sobre a resposta aceita e não deveria ter sido feito como uma tentativa de resposta separada.
— Michael Gaskill

1
Apenas uma coisa a adicionar aqui, isso funciona muito bem, mas você deve ler o tamanho do buffer - 1 byte. E para ver isso corretamente, eu não usaria uma nova linha nessa declaração de impressão. Deve ser assim:bytes = recv(sockfd, response, 1023, 0)
— xjsc16x

11

Após semanas de pesquisa. Eu vim com o seguinte código. Eu acredito que este é o mínimo necessário para fazer uma conexão segura com SSL para um servidor web.

O código acima explicará em detalhes como estabelecer uma conexão TLS com um servidor remoto.

Nota importante : este código não verifica se a chave pública foi assinada por uma autoridade válida. O que significa que não uso certificados raiz para validação. Não se esqueça de implementar esta verificação, caso contrário você não saberá se está conectando o site certo

Quando se trata do próprio pedido. Nada mais é do que escrever a solicitação HTTP manualmente.

Você também pode encontrar neste link uma explicação de como instalar o openSSL em seu sistema e como compilar o código para que ele use a biblioteca segura .


2
Boa explicação!
— Satyam Koyani

não, a próxima variável é a porta, já estamos conectando na porta certa.
— David Gatti

3

Alça adicionada.
Adicionado cabeçalho de host.
Adicionado suporte a linux / windows, testado (XP, WIN7).
AVISO: ERRO: "falha de segmentação" se não houver host, caminho ou porta como argumento.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.