練習:利用某些平臺(聚合 API、百度 A、科大訊飛 API)的 API介面,利用 HTTP 協議向伺服器傳送請求,並接受伺服器的響應,要求利用cISON庫對伺服器的響應資料進行解析,並輸出到終端。
/*****************************************************************************************************
* Function: parse_json_response
* Description: 解析從HTTP響應中接收到的JSON字串,並列印出格式化的JSON以及提取的關鍵資訊。
* Calls: cJSON_Parse, cJSON_Print, cJSON_GetObjectItem, cJSON_GetArrayItem, cJSON_Delete
* Called By: main
* Input: const char *response - 指向包含HTTP頭和JSON響應體的完整響應字串的指標。
* Output: 列印到標準輸出的格式化JSON字串和提取的關鍵資訊。
* Return: void - 此函式不返回任何值。
* Others:
* 1.待解析的JSON
* {
* "results": [{
* "id": "WX4FBXXFKE4F",
* "name": "北京",
* "country": "CN",
* "path": "北京,北京,中國",
* "timezone": "Asia/Shanghai",
* "timezone_offset": "+08:00"
* }]
* }
* 2.此函式假設response引數是一個有效的HTTP響應,包含一個JSON響應體。
* 如果response格式不正確或解析失敗,將列印錯誤資訊到標準錯誤。
* 3.注意HTTP請求後會有HTTP響應頭和JSON響應體,在JSON解析時要分離出來
* 4.請求資訊過於大時,會申請的堆空間可能會無法滿足,要考慮空間大小
* 5.當HTTP回覆的訊息不完整時,會出現段錯誤,要讓recv()函式迴圈接收完全。
* 6.將宏定義中的KEY,替換成自己的
* 7.本文采用“心知天氣”的相關API
*
* CopyRight (c) 2023-2024 User_laubon@163.com All Right Reserved
*********************************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "cJSON.h"
#define PORT 80
#define HOSTNAME "api.seniverse.com"
#define KEY "your_key"
#define LOCAL "guangzhou"
#define BUFFSIZE 102400
/************************************************** 建立TCP套接字 **********************************************************/
int create_tcp_socket() {
int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
if (tcp_socket == -1) {
fprintf(stderr, "tcp socket error, errno:%d, %s\n", errno, strerror(errno));
exit(1);
}
return tcp_socket;
}
/************************************************** 解析主機名 **********************************************************/
struct sockaddr_in resolve_hostname(const char *hostname) {
struct hostent *he = gethostbyname(hostname);
if (he == NULL) {
fprintf(stderr, "gethostbyname error\n");
exit(1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr = *((struct in_addr *)he->h_addr);
return addr;
}
/************************************************** 連線到伺服器 **********************************************************/
void connect_to_server(int socket, struct sockaddr_in addr) {
if (connect(socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
fprintf(stderr, "connect error, errno:%d, %s\n", errno, strerror(errno));
exit(1);
}
}
/************************************************** 傳送HTTP請求 **********************************************************/
void send_http_request(int socket, const char *hostname, const char *key) {
char reqbuf[1024];
sprintf(reqbuf,
"GET https://api.seniverse.com/v3/location/search.json?key=%s&q=WX4FBXXFKE4F HTTP/1.1\r\n"
"Host:%s\r\n"
"\r\n",
key, hostname);
if (send(socket, reqbuf, strlen(reqbuf), 0) < 0) {
fprintf(stderr, "send error, errno:%d, %s\n", errno, strerror(errno));
exit(1);
}
}
/************************************************** 接收HTTP響應 **********************************************************/
char *receive_http_response(int socket) {
char *response = malloc(BUFFSIZE); // 分配記憶體以接收響應
if (response == NULL) {
fprintf(stderr, "malloc failed\n");
exit(1);
}
int bytes_received;
int total_bytes_received = 0;
do {
bytes_received = recv(socket, response + total_bytes_received, BUFFSIZE - total_bytes_received - 1, 0);
if (bytes_received > 0) {
total_bytes_received += bytes_received;
}
} while (bytes_received > 0);
if (bytes_received < 0) {
fprintf(stderr, "recv error, errno:%d, %s\n", errno, strerror(errno));
free(response);
exit(1);
}
response[total_bytes_received] = '\0'; // 確保響應字串以空字元結尾
return response;
}
/************************************************** 解析JSON響應 **********************************************************/
void parse_json_response(const char *response) {
// 查詢HTTP響應頭和響應體之間的空行,將HTTP響應頭和JSON響應體分開
const char *json_data = strstr(response, "\r\n\r\n");
if (json_data != NULL) {
json_data += 4; // 跳過空行,指向JSON資料的開始
} else {
fprintf(stderr, "Failed to find the end of the HTTP headers.\n");
return;
}
cJSON *json = cJSON_Parse(json_data);
if (json == NULL) {
fprintf(stderr, "Error before: [%s]\n", cJSON_GetErrorPtr());
} else {
char *json_str = cJSON_Print(json);
printf("%s\n", json_str); // 列印格式化的JSON字串
cJSON * result = cJSON_GetObjectItem(json,"results");
cJSON * array = cJSON_GetArrayItem(result,0);
cJSON * id = cJSON_GetObjectItem(array,"id");
cJSON * name = cJSON_GetObjectItem(array,"name");
cJSON * country = cJSON_GetObjectItem(array,"country");
cJSON * path = cJSON_GetObjectItem(array,"path");
cJSON * timezone = cJSON_GetObjectItem(array,"timezone");
cJSON * timezone_offset = cJSON_GetObjectItem(array,"timezone_offset");
printf("id : %s\n", id->valuestring);
printf("name : %s\n", name->valuestring);
printf("country : %s\n", country->valuestring);
printf("path : %s\n", path->valuestring);
printf("timezone : %s\n", timezone->valuestring);
printf("timezone_offset : %s\n", timezone_offset->valuestring);
free(json_str);
cJSON_Delete(json);
}
}
int main(int argc, char const *argv[]) {
int tcp_socket = create_tcp_socket();
struct sockaddr_in server_addr = resolve_hostname(HOSTNAME);
connect_to_server(tcp_socket, server_addr);
send_http_request(tcp_socket, HOSTNAME, KEY);
char *response = receive_http_response(tcp_socket);
parse_json_response(response);
free(response); // 釋放接收到的響應
close(tcp_socket); // 關閉套接字
return 0;
}