Semplice esempio in C di esecuzione di un POST HTTP e consumo della risposta


92

Vorrei creare un'applicazione C molto semplice che faccia un post HTTP. Ci vorranno alcuni parametri e li useremo per costruire un URL. Vorrei solo fare un semplice HTTP POST e ottenere la risposta senza l'uso di curl (le librerie non sono e non verranno installate sulla macchina che deve essere eseguita).

Pseudo-codice:

  1. Elabora 2 argomenti

  2. Inserisci gli argomenti nell'URL del modello: http://api.somesite.com/apikey=ARG1&command=ARG2

  3. POST sull'URL generato

  4. Consumare la risposta

Le mie ricerche su Google e SO non hanno prodotto nulla su questo argomento.


2
Usi qualche tipo di framework di rete? Che sistema operativo usi?
cnicutar

Sarà solo una scatola Fedora o Cent di base. I framework di rete sono i soliti sys / socket, netdb, arpa / inet. Solo non libcurl.
kmarks2

1
Non libcurl. Sei disposto a utilizzare qualsiasi altra libreria o deve essere interamente POSIX.
cnicutar

Sfortunatamente, tutti i POSIX. Deve essere completamente indipendente su qualsiasi sistema.
kmarks2

2
Ho un campione che ho creato per te ma non capisco perché stai usando POST se non c'è un corpo per il messaggio. Se tutti i parametri sono nella stringa di query, perché non vuoi fare un GET?
Jerry Jeremiah

Risposte:


196

Un messaggio ha una parte di intestazione e un corpo del messaggio separati da una riga vuota. La riga vuota è SEMPRE necessaria anche se non c'è il corpo del messaggio. L'intestazione inizia con un comando e ha righe aggiuntive di coppie di valori chiave separate da due punti e uno spazio. Se è presente un corpo del messaggio, può essere qualsiasi cosa tu voglia che sia.

Le righe nell'intestazione e la riga vuota alla fine dell'intestazione devono terminare con un ritorno a capo e una coppia di avanzamento riga (vedere lo stile di interruzione di riga dell'intestazione HTTP ), quindi è per questo che quelle righe hanno \ r \ n alla fine.

Un URL ha la forma di http://host:port/path?query_string

Esistono due modi principali per inviare una richiesta a un sito Web:

  • GET: la stringa della query è facoltativa ma, se specificata, deve essere ragionevolmente breve. Per questo motivo l'intestazione potrebbe essere solo il comando GET e nient'altro. Un messaggio di esempio potrebbe essere:

  • POST: ciò che normalmente sarebbe nella stringa di query è invece nel corpo del messaggio. Per questo motivo, l'intestazione deve includere gli attributi Content-Type: e Content-Length: oltre al comando POST. Un messaggio di esempio potrebbe essere:

Quindi, per rispondere alla tua domanda: se l'URL a cui sei interessato a POST è http://api.somesite.com/apikey=ARG1&command=ARG2, allora non c'è il corpo o la stringa di query e, di conseguenza, nessun motivo per POST perché lì non è niente da mettere nel corpo del messaggio e quindi niente da mettere in Content-Type: e Content-Length:

Immagino che potresti POST se lo volessi davvero. In tal caso il tuo messaggio sarebbe simile a:

Quindi, per inviare il messaggio, il programma C deve:

  • creare un socket
  • cercare l'indirizzo IP
  • aprire la presa
  • invia la richiesta
  • attendere la risposta
  • chiudere la presa

Le chiamate di invio e ricezione non invieranno / riceveranno necessariamente TUTTI i dati forniti loro - restituiranno il numero di byte effettivamente inviati / ricevuti. Spetta a te chiamarli in loop e inviare / ricevere il resto del messaggio.

Quello che non ho fatto in questo esempio è alcun tipo di controllo degli errori reali: quando qualcosa non funziona, esco semplicemente dal programma. Fammi sapere se funziona per te:

Come l'altra risposta sottolineata, 4096 byte non è una risposta molto grande. Ho scelto quel numero a caso supponendo che la risposta alla tua richiesta sarebbe stata breve. Se può essere grande hai due scelte:

  • leggere il Content-Length: header dalla risposta e quindi allocare dinamicamente abbastanza memoria per contenere l'intera risposta.
  • scrivere la risposta su un file non appena arrivano i pezzi

Ulteriori informazioni per rispondere alla domanda posta nei commenti:

Cosa succede se si desidera POST dei dati nel corpo del messaggio? Quindi è necessario includere le intestazioni Content-Type: e Content-Length:. Content-Length: è la lunghezza effettiva di tutto ciò che segue la riga vuota che separa l'intestazione dal corpo.

Ecco un esempio che accetta i seguenti argomenti della riga di comando:

  • ospite
  • porta
  • comando (GET o POST)
  • percorso (esclusi i dati della query)
  • dati della query (inseriti nella stringa della query per GET e nel corpo per POST)
  • elenco di intestazioni (Content-Length: è automatico se si utilizza POST)

Quindi, per la domanda originale dovresti eseguire:

E per la domanda posta nei commenti dovresti eseguire:

Ecco il codice:


Quali argomenti dovrebbero essere passati quando chiamati?
Santiago Martí Olbrich

Devi passare qualcosa che verrà utilizzato come apikey come primo parametro e qualcosa nel secondo parametro che verrà utilizzato come comando. Se si desidera utilizzare una stringa di query completamente diversa, è necessario modificare la stringa di formato, il numero di parametri e il messaggio di utilizzo.
Jerry Jeremiah

2
Questo codice emette una richiesta HTTP non valida. HTTP specifica che le righe di richiesta devono essere terminate da coppie di ritorno a capo / avanzamento riga ( \r\n), ma questo codice utilizza semplici feed di riga.
John Bollinger

@JohnBollinger Questo è molto vero. Grazie per segnalarlo. Si spera che la risposta modificata sia migliore.
Jerry Jeremiah

Cosa c'è di sbagliato in questo messaggio di posta? "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"Voglio pubblicare come nome = reaz. Risponde 400 Bad Request
Reaz Murshed

12

La risposta di Jerry è fantastica. Tuttavia, non gestisce risposte di grandi dimensioni. Una semplice modifica per gestire questo:


3
Potresti semplicemente ingrandire la matrice di risposta nel mio esempio. Stavo presumendo che stesse recuperando un po 'di json e non stesse scaricando un file enorme, ma ovviamente anche json può essere megabyte a seconda della query ...
Jerry Jeremiah

1
Sono un principiante C e la tua risposta potrebbe essere corretta. Ma puoi aggiungere una spiegazione alla tua risposta?
Boop

2
Questo è in realtà solo un commento sulla risposta accettata e non avrebbe dovuto essere fatto come un tentativo di risposta separato.
Michael Gaskill

1
Solo una cosa da aggiungere qui, funziona benissimo, tuttavia dovresti leggere la dimensione del buffer: 1 byte. E per visualizzarlo correttamente non userei una nuova riga in quella dichiarazione di stampa. Dovrebbe assomigliare a questo:bytes = recv(sockfd, response, 1023, 0)
xjsc16x

11

Dopo settimane di ricerca. Mi è venuto in mente il seguente codice. Credo che questo sia il minimo indispensabile per stabilire una connessione sicura con SSL a un server web.

Il codice sopra spiegherà in dettaglio come stabilire una connessione TLS con un server remoto.

Nota importante : questo codice non controlla se la chiave pubblica è stata firmata da un'autorità valida. Significa che non uso i certificati radice per la convalida. Non dimenticare di implementare questo controllo altrimenti non saprai se stai collegando il sito web giusto

Quando si tratta della richiesta stessa. Non è altro che scrivere a mano la richiesta HTTP.

Sotto questo collegamento puoi anche trovare una spiegazione su come installare openSSL nel tuo sistema e come compilare il codice in modo che utilizzi la libreria sicura .


2
Bella spiegazione!
Satyam Koyani

no, la prossima variabile è la porta, ci stiamo già collegando alla porta giusta.
David Gatti

3

Maniglia aggiunta.
Intestazione host aggiunta.
Aggiunto supporto linux / windows, testato (XP, WIN7).
ATTENZIONE: ERRORE: "errore di segmentazione" se nessun host, percorso o porta come argomento.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.