我想创建一个非常简单的C语言应用程序,做一个HTTP帖子。 它将接受一些参数,并使用这些参数来构建一个URL。 我只想做一个简单的HTTP POST,并在不使用curl的情况下得到响应(在需要运行的机器上没有也不会安装这些库)。
伪代码:
1.处理2个args
2.将args放入模板URL:http://api.somesite.com/apikey=ARG1&command=ARG2
3.对生成的URL进行POST操作
4.吸收响应
我在Google和SO的搜索中没有得到任何关于这个问题的信息。
一条信息有一个标题部分和一个信息主体,中间有一个空行。 即使没有消息正文,也总是需要空行。 头部以一个命令开始,并有另外几行键值对,用冒号和空格隔开。 如果有一个信息主体,它可以是你想要的任何内容。
标题中的行和标题末尾的空白行必须以回车和换行对结束(见https://stackoverflow.com/questions/5757290/http-header-line-break-style),所以这就是为什么这些行的末尾有 \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\n
Content-Length: 12\r\n
\query_string
query_string
因此,为了回答你的问题:如果你感兴趣的URL是http://api.somesite.com/apikey=ARG1&command=ARG2,那么就没有正文或查询字符串,因此也就没有理由进行POST,因为没有任何东西可以放在消息的正文中,所以也没有任何东西可以放在Content-Type:和Content-Length中:
我想如果你真的想,你可以POST。 在这种情况下,你的信息会看起来像:
POST /apikey=ARG1&command=ARG2 HTTP/1.0\r\n
\r\n
因此,为了发送消息,C程序需要:
发送和接收调用不一定会发送/接收你给它们的所有数据--它们将返回实际发送/接收的字节数。 你可以在一个循环中调用它们并发送/接收剩余的信息。
在这个例子中,我没有做任何真正的错误检查--当某些东西失败时,我只是退出程序。 让我知道它是否对你有用:
#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;
}
杰里的回答很好。然而,它并不能处理大型的回应。一个简单的改变就可以处理这个问题:
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);
添加了处理程序。<br/>; 添加了主机头。<br/>; 增加了对linux/windows的支持,经过测试(XP,WIN7)。<br/>; WARNING: ERROR : "segmentation fault" if no host, path or port as argument.
#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;
}