Thư có phần tiêu đề và phần nội dung thư được phân tách bằng một dòng trống. Dòng trống LUÔN LUÔN cần thiết ngay cả khi không có nội dung thư. Tiêu đề bắt đầu bằng một lệnh và có các dòng bổ sung của các cặp giá trị khóa được phân tách bằng dấu hai chấm và dấu cách. Nếu có một nội dung thư, nó có thể là bất cứ thứ gì bạn muốn.
Các dòng trong tiêu đề và dòng trống ở cuối tiêu đề phải kết thúc bằng cặp dòng trả về và nguồn cấp dữ liệu dòng (xem kiểu ngắt dòng tiêu đề HTTP ), đó là lý do tại sao các dòng đó có \ r \ n ở cuối.
URL có dạng http://host:port/path?query_string
Có hai cách chính để gửi yêu cầu đến một trang web:
GET: Chuỗi truy vấn là tùy chọn nhưng, nếu được chỉ định, phải ngắn hợp lý. Do đó, tiêu đề chỉ có thể là lệnh GET và không có gì khác. Một thông báo mẫu có thể là:
GET /path?query_string HTTP/1.0\r\n
\r\n
ĐĂNG: Thay vào đó, những gì thường có trong chuỗi truy vấn nằm trong phần nội dung của thư. Do đó, tiêu đề cần phải bao gồm các thuộc tính Content-Type: và Content-Length: cũng như lệnh POST. Một thông báo mẫu có thể là:
POST /path HTTP/1.0\r\n
Content-Type: text/plain\r\n
Content-Length: 12\r\n
\r\n
query_string
Vì vậy, để trả lời câu hỏi của bạn: nếu URL mà bạn muốn ĐĂNG đến là http://api.somesite.com/apikey=ARG1&command=ARG2 thì không có nội dung hoặc chuỗi truy vấn và do đó, không có lý do gì để ĐĂNG vì ở đó không có gì để đưa vào nội dung thư và vì vậy không có gì để đưa vào Loại nội dung: và Nội dung-Độ dài:
Tôi đoán bạn có thể ĐĂNG nếu bạn thực sự muốn. Trong trường hợp đó, tin nhắn của bạn sẽ giống như sau:
POST /apikey=ARG1&command=ARG2 HTTP/1.0\r\n
\r\n
Vì vậy, để gửi thông điệp, chương trình C cần:
- tạo một ổ cắm
- tra cứu địa chỉ IP
- mở ổ cắm
- gửi yêu cầu
- đợi phản hồi
- đóng ổ cắm
Cuộc gọi gửi và nhận sẽ không nhất thiết phải gửi / nhận TẤT CẢ dữ liệu bạn cung cấp cho họ - chúng sẽ trả về số byte thực sự đã gửi / nhận. Bạn có thể gọi điện theo vòng lặp và gửi / nhận phần còn lại của tin nhắn.
Những gì tôi đã không làm trong mẫu này là bất kỳ loại kiểm tra lỗi thực sự nào - khi có điều gì đó không thành công, tôi chỉ cần thoát khỏi chương trình. Hãy cho tôi biết nếu nó làm việc cho bạn:
#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[])
{
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); }
sprintf(message,message_fmt,argv[1],argv[2]);
printf("Request:\n%s\n",message);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("ERROR opening socket");
server = gethostbyname(host);
if (server == NULL) error("ERROR, no such host");
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);
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
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);
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(sockfd);
printf("Response:\n%s\n",response);
return 0;
}
Giống như câu trả lời khác đã chỉ ra, 4096 byte không phải là một phản hồi quá lớn. Tôi đã chọn con số đó một cách ngẫu nhiên với giả định rằng phản hồi cho yêu cầu của bạn sẽ ngắn. Nếu nó có thể lớn, bạn có hai lựa chọn:
- đọc tiêu đề Content-Length: từ phản hồi và sau đó tự động phân bổ đủ bộ nhớ để chứa toàn bộ phản hồi.
- ghi phản hồi vào một tệp khi các phần đến
Thông tin bổ sung để trả lời câu hỏi được hỏi trong phần bình luận:
Điều gì xảy ra nếu bạn muốn ĐĂNG dữ liệu trong phần nội dung thư? Sau đó, bạn cần bao gồm tiêu đề Content-Type: và Content-Length:. Nội dung-Độ dài: là độ dài thực tế của mọi thứ sau dòng trống ngăn cách tiêu đề khỏi nội dung.
Đây là một mẫu lấy các đối số dòng lệnh sau:
- tổ chức
- Hải cảng
- lệnh (GET hoặc POST)
- đường dẫn (không bao gồm dữ liệu truy vấn)
- dữ liệu truy vấn (đưa vào chuỗi truy vấn cho GET và vào phần nội dung cho POST)
- danh sách các tiêu đề (Nội dung-Độ dài: là tự động nếu sử dụng POST)
Vì vậy, đối với câu hỏi ban đầu, bạn sẽ chạy:
a.out api.somesite.com 80 GET "/apikey=ARG1&command=ARG2"
Và đối với câu hỏi được hỏi trong phần bình luận, bạn sẽ chạy:
a.out api.somesite.com 80 POST / "name=ARG1&value=ARG2" "Content-Type: application/x-www-form-urlencoded"
Đây là mã:
#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;
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); }
message_size=0;
if(!strcmp(argv[3],"GET"))
{
message_size+=strlen("%s %s%s%s HTTP/1.0\r\n");
message_size+=strlen(argv[3]);
message_size+=strlen(argv[4]);
if(argc>5)
message_size+=strlen(argv[5]);
for(i=6;i<argc;i++)
message_size+=strlen(argv[i])+strlen("\r\n");
message_size+=strlen("\r\n");
}
else
{
message_size+=strlen("%s %s HTTP/1.0\r\n");
message_size+=strlen(argv[3]);
message_size+=strlen(argv[4]);
for(i=6;i<argc;i++)
message_size+=strlen(argv[i])+strlen("\r\n");
if(argc>5)
message_size+=strlen("Content-Length: %d\r\n")+10;
message_size+=strlen("\r\n");
if(argc>5)
message_size+=strlen(argv[5]);
}
message=malloc(message_size);
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",
strlen(argv[4])>0?argv[4]:"/",
strlen(argv[5])>0?"?":"",
strlen(argv[5])>0?argv[5]:"");
else
sprintf(message,"%s %s HTTP/1.0\r\n",
strlen(argv[3])>0?argv[3]:"GET",
strlen(argv[4])>0?argv[4]:"/");
for(i=6;i<argc;i++)
{strcat(message,argv[i]);strcat(message,"\r\n");}
strcat(message,"\r\n");
}
else
{
sprintf(message,"%s %s HTTP/1.0\r\n",
strlen(argv[3])>0?argv[3]:"POST",
strlen(argv[4])>0?argv[4]:"/");
for(i=6;i<argc;i++)
{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");
if(argc>5)
strcat(message,argv[5]);
}
printf("Request:\n%s\n",message);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("ERROR opening socket");
server = gethostbyname(host);
if (server == NULL) error("ERROR, no such host");
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);
if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
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);
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(sockfd);
printf("Response:\n%s\n",response);
free(message);
return 0;
}