HTTP POST를 실행하여 응답을 소비하는 간단한 C 예시
HTTP 포스트를 하는 매우 간단한 C 어플리케이션을 만들고 싶습니다.몇 가지 매개 변수를 사용하여 URL을 구성합니다. 간단한 HTTP POST를 수행하여 컬을 사용하지 않고 응답을 받고 싶습니다(라이브러리는 이 실행이 필요한 머신에 설치되지 않습니다).
유사 코드:
프로세스 2 args
arg를 템플릿 URL에 넣습니다.http://api.somesite.com/apikey=ARG1&command=ARG2
생성된 URL에서 POST 실행
소비 응답
내 구글과 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
따라서 POST에 관심이 있는 URL이 http://api.somesite.com/apikey=ARG1&command=ARG2인 경우 본문 또는 쿼리 문자열이 없으며 메시지 본문에 입력할 내용이 없으므로 Content-Type: 및 Content-Length에 입력할 내용이 없기 때문에 POST할 이유가 없습니다.
당신이 정말 원한다면 POST를 할 수 있을 것 같아요.이 경우 메시지는 다음과 같습니다.
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 the number of received bytes is the total size of the
* array then we have run out of space to store the response
* and it hasn't all arrived yet - so that's a bad thing
*/
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 the number of received bytes is the total size of the
* array then we have run out of space to store the response
* and it hasn't all arrived yet - so that's a bad thing
*/
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;
}
몇 주간의 연구 끝에.나는 다음과 같은 코드를 생각해냈다.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 요청을 손으로 작성하는 것에 지나지 않습니다.
또한 이 링크에서 설치 방법을 찾을 수 있습니다.시스템의 SSL 및 보안 라이브러리를 사용하도록 코드를 컴파일하는 방법.
제리의 대답은 훌륭하다.그러나 큰 응답은 처리하지 않습니다.이를 처리하기 위한 간단한 변경:
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);
핸들이 추가되었습니다.
호스트 헤더가 추가되었습니다.
Linux/Windows 지원 추가, 테스트 완료(XP, WIN7)
경고: ERROR: 인수로서 호스트, 패스 또는 포트가 없는 경우 "세그멘테이션 장애"
#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;
}
언급URL : https://stackoverflow.com/questions/22077802/simple-c-example-of-doing-an-http-post-and-consuming-the-response
'programing' 카테고리의 다른 글
Vue 구성 요소에 Vuelidate $v가 정의되지 않았습니다. (0) | 2022.07.07 |
---|---|
JS 파일이 외부에서 로드된 경우 Vue에서 공동 구성 요소를 등록할 수 없습니다. (0) | 2022.07.07 |
C++ 프로그램에서 scanf()를 사용하는 것이 cin을 사용하는 것보다 빠릅니다. (0) | 2022.07.06 |
JPA의 열에 대한 기본값 설정 (0) | 2022.07.06 |
마운트되었지만 프로덕션 환경에서는 렌더링되지 않은 템플릿 태그(개발 환경에서는 렌더링됨):Nuxtjs Vuejs Vuetifyjs 롤업즈 (0) | 2022.07.06 |