Linux:网络编程-基于HTTP协议的天气预报查询系统开发详解
目录
1.系统架构与核心功能
2.关键技术实现
2.1网络通信
2.2JSON数据解析
3.完整工作流程
4.技术亮点
5.成果展示
5.1代码
5.2使用示例
6.扩展方向
引言
在嵌入式开发或网络编程学习中,通过Socket编程实现与Web API的交互是一项基础而重要的技能。本文介绍了我开发的一个基于HTTP协议的天气预报查询系统。该系统通过以下流程获取数据:
建立TCP连接至远程服务器(nowapi提供的API接口)
发送符合规范的HTTP请求
接收并解析JSON格式的响应数据
提取并展示指定城市的实时天气及未来天气预报
下文将详细解析系统实现的技术细节,包括Socket通信机制、HTTP协议处理、JSON数据解析等核心模块的实现方案。
1.系统架构与核心功能
该系统采用模块化设计,主要包含以下功能模块:
网络通信模块:建立TCP连接并发送HTTP请求
数据解析模块:处理JSON格式的天气数据
数据展示模块:格式化输出天气信息
程序通过Weather结构体存储天气数据:
typedef struct { char city[32]; char date[32]; char week[32]; char weather[32]; char max_temperature[4]; char min_temperature[4]; char wind_direction[32]; char wind_power[8]; char Humidity[8]; } Weather;2.关键技术实现
2.1网络通信
使用
socket建立TCP连接通过
connect连接nowapi服务器(103.205.5.206:80)构造符合HTTP协议的GET请求
int CreateTcpConnection(const char *pip, int port) { int sockfd = socket(AF_INET, SOCK_STREAM, 0); // ...套接字创建和连接逻辑 } int SendHttpRequest(int sockfd, const char *purl) { sprintf(sendbuf, "GET %s HTTP/1.1\r\n", purl); // ...HTTP头构造逻辑 }2.2JSON数据解析
使用cJSON库解析API返回的JSON数据
分别处理当天和未来天气数据
错误处理机制确保数据有效性
int Analys_today(char* json_string) { cJSON *root = cJSON_Parse(json_string); cJSON *success = cJSON_GetObjectItem(root, "success"); if(0 == strcmp(success->valuestring,"0")) { printf("该地点未收到天气数据\n"); exit(0); } // ...数据提取逻辑 }2.3用户交互与展示
终端交互式输入城市名称
格式化输出当天和未来天气信息
清晰的数据分隔线提升可读性
int ShowToday() { printf("------------------------------------------------\n"); printf("城市 : %s\n",Today.city); printf("日期 : %s\n",Today.date); // ...其他天气信息输出 printf("------------------------------------------------\n"); }3.完整工作流程
用户输入目标城市名称
程序创建TCP连接并发送天气请求:
当天天气:
/?app=weather.today&weaid=城市名...未来天气:
/?app=weather.future&weaid=城市名...
接收并解析JSON格式的天气数据
格式化输出天气信息:
当天天气详情
未来7天天气预报
4.技术亮点
高效的网络通信:直接使用socket进行TCP通信,避免依赖外部库
健壮的错误处理:检测API返回的success字段,无效数据时优雅退出
内存管理:使用cJSON_Delete释放解析后的JSON对象
数据封装:Weather结构体清晰组织天气数据
5.成果展示
5.1代码
#include "head.h" #include "cJSON.h" typedef struct { char city[32]; char date[32]; char week[32]; char weather[32]; char max_temperature[4]; char min_temperature[4]; char wind_direction[32]; char wind_power[8]; //风力级数 char Humidity[8]; //湿度 }Weather; Weather Today ; Weather FutureDay[7] = {0}; int Num_future_days = 0; int CreateTcpConnection(const char *pip, int port) { int ret = 0; int sockfd = 0; struct sockaddr_in seraddr; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (-1 == sockfd) { perror("fail to socket"); return -1; } seraddr.sin_family = AF_INET; seraddr.sin_port = htons(port); seraddr.sin_addr.s_addr = inet_addr(pip); ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr)); if (-1 == ret) { perror("fail to connect"); return -1; } return sockfd; } int SendHttpRequest(int sockfd, const char *purl) //发送http请求 { char sendbuf[4096] = {0}; ssize_t nret = 0; sprintf(sendbuf, "GET %s HTTP/1.1\r\n", purl); sprintf(sendbuf, "%sHost: api.k780.com\r\n", sendbuf); sprintf(sendbuf, "%sUser-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/113.0\r\n", sendbuf); sprintf(sendbuf, "%sAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\n", sendbuf); sprintf(sendbuf, "%sAccept-Language: en-US,en;q=0.5\r\n", sendbuf); sprintf(sendbuf, "%sAccept-Encoding: gzip, deflate\r\n", sendbuf); sprintf(sendbuf, "%sConnection: keep-alive\r\n", sendbuf); sprintf(sendbuf, "%sUpgrade-Insecure-Requests: 1\r\n", sendbuf); sprintf(sendbuf, "%s\r\n", sendbuf); nret = send(sockfd, sendbuf, strlen(sendbuf), 0); if (-1 == nret) { perror("fail to send"); return -1; } return 0; } int RecvHttpRespone(int sockfd, char *ptmpbuff, int maxlen) { ssize_t nret = 0; nret = recv(sockfd, ptmpbuff, maxlen, 0); if (-1 == nret) { perror("fail to recv"); return -1; } //printf("%s\n\n",ptmpbuff); return 0; } int ShowToday( ) { printf("----------------------今日天气-------------------\n"); printf("------------------------------------------------\n"); printf("城市 : %s\n",Today.city); printf("日期 : %s\n",Today.date); printf("星期 : %s\n",Today.week); printf("天气 : %s\n",Today.weather); printf("最高温度 :%s℃\n",Today.max_temperature); printf("最低温度 :%s℃\n",Today.min_temperature); printf("湿度 : %s\n",Today.Humidity); printf("风向 : %s\n",Today.wind_direction); printf("风力 : %s\n",Today.wind_power); printf("------------------------------------------------\n"); return 0; } int ShowFuture() { printf("----------------------未来天气-------------------\n"); // 遍历数组 for(int i = 1; i < Num_future_days; i++) { printf("------------------------------------------------\n"); printf("日期 : %s\n",FutureDay[i].date); printf("星期 : %s\n",FutureDay[i].week); printf("天气 : %s\n",FutureDay[i].weather); printf("最高温度 : %s℃\n",FutureDay[i].max_temperature); printf("最低温度 : %s℃\n",FutureDay[i].min_temperature); printf("风向 : %s\n",FutureDay[i].wind_direction); printf("风力 : %s\n",FutureDay[i].wind_power); printf("------------------------------------------------\n"); } return 0; } // 解析JSON字符串 int Analys_today(char* json_string) { cJSON *root = cJSON_Parse(json_string); if (!root) { printf("解析失败\n"); return -1; } cJSON *success = cJSON_GetObjectItem(root, "success"); if(0 == strcmp(success -> valuestring,"0")) { printf("================================================\n"); printf("该地点未收到天气数据\n"); printf("================================================\n"); exit(0);//优雅退出进程 } cJSON *result = cJSON_GetObjectItem(root, "result"); // 获取今日的结果 strcpy(Today.date , cJSON_GetObjectItem(result, "days")->valuestring); strcpy(Today.city , cJSON_GetObjectItem(result, "citynm")->valuestring); strcpy(Today.week , cJSON_GetObjectItem(result, "week")->valuestring); strcpy(Today.weather , cJSON_GetObjectItem(result, "weather")->valuestring); strcpy(Today.wind_direction , cJSON_GetObjectItem(result, "wind")->valuestring); strcpy(Today.wind_power, cJSON_GetObjectItem(result, "winp")->valuestring); strcpy(Today.Humidity , cJSON_GetObjectItem(result, "humidity")->valuestring); strcpy(Today.max_temperature , cJSON_GetObjectItem(result, "temp_high")->valuestring); strcpy(Today.min_temperature , cJSON_GetObjectItem(result, "temp_low")->valuestring); cJSON_Delete(root); return 0; } int Analys_future(char* json_string) { // 解析JSON字符串 char tmp[32] = {0}; cJSON *root = cJSON_Parse(json_string); cJSON *result = cJSON_GetObjectItem(root, "result");//遍历数组 if (!root) { printf("解析失败\n"); return -1; } // 获取未来的结果 // 遍历数组 Num_future_days = cJSON_GetArraySize(result); for (int i = 0; i < Num_future_days; i++) { cJSON *futureDay_tmp = cJSON_GetArrayItem(result, i);//遍历数组 //获取未来天气结果 strcpy(FutureDay[i].date , cJSON_GetObjectItem(futureDay_tmp, "days")->valuestring); strcpy(FutureDay[i].week , cJSON_GetObjectItem(futureDay_tmp, "week")->valuestring); strcpy(FutureDay[i].weather , cJSON_GetObjectItem(futureDay_tmp, "weather")->valuestring); strcpy(FutureDay[i].wind_direction , cJSON_GetObjectItem(futureDay_tmp, "wind")->valuestring); strcpy(FutureDay[i].wind_power, cJSON_GetObjectItem(futureDay_tmp, "winp")->valuestring); strcpy(FutureDay[i].max_temperature , cJSON_GetObjectItem(futureDay_tmp, "temp_high")->valuestring); strcpy(FutureDay[i].min_temperature , cJSON_GetObjectItem(futureDay_tmp, "temp_low")->valuestring); } // 释放内存 cJSON_Delete(root); return 0; } int main(void) { int sockfd = 0; char tmpbuff[40960] = {0}; char City[32] = {0}; char URL[256] = {0}; char* j_start = NULL; printf("请输入您要查看的城市:\n"); scanf("%s",City); //发送请求 sockfd = CreateTcpConnection("103.205.5.206", 80); sprintf(URL,"/?app=weather.today&weaid=%s&appkey=78692&sign=4fc12179d774cba5500d9d7c56e92f74&format=json",City); SendHttpRequest(sockfd, URL); RecvHttpRespone(sockfd, tmpbuff, sizeof(tmpbuff));//接收结果 //close(sockfd); //解析结果 j_start = strstr(tmpbuff, "\r\n\r\n"); j_start += 4; // 跳过"\r\n\r\n" for(;*j_start != '{';j_start++); //输出结果 Analys_today(j_start); printf("================================================\n"); ShowToday(); //发送请求 memset(tmpbuff,0,sizeof(tmpbuff)); memset(URL,0,sizeof(URL)); sprintf(URL,"/?app=weather.future&weaid=%s&appkey=78692&sign=4fc12179d774cba5500d9d7c56e92f74&format=json",City); SendHttpRequest(sockfd, URL); RecvHttpRespone(sockfd, tmpbuff, sizeof(tmpbuff));//接收结果 //解析结果 j_start = strstr(tmpbuff, "\r\n\r\n"); j_start += 4; // 跳过"\r\n\r\n" for(;*j_start != '{';j_start++); //输出结果 Analys_future(j_start); ShowFuture(); printf("================================================\n"); close(sockfd); return 0; }5.2使用示例
// ./weather_program 请输入您要查看的城市: 泸州 ================================================ ----------------------今日天气------------------- ------------------------------------------------ 城市 : 泸州 日期 : 2026-03-08 星期 : 星期日 天气 : 小雨转中雨 最高温度 :16℃ 最低温度 :12℃ 湿度 : 67% 风向 : 东北风 风力 : 2级 ------------------------------------------------ ----------------------未来天气------------------- ------------------------------------------------ 日期 : 2026-03-09 星期 : 星期一 天气 : 小雨 最高温度 : 17℃ 最低温度 : 10℃ 风向 : 无持续风向 风力 : 小于3级 ------------------------------------------------ ------------------------------------------------ 日期 : 2026-03-10 星期 : 星期二 天气 : 晴 最高温度 : 24℃ 最低温度 : 11℃ 风向 : 无持续风向 风力 : 小于3级 ------------------------------------------------ ------------------------------------------------ 日期 : 2026-03-11 星期 : 星期三 天气 : 晴转多云 最高温度 : 25℃ 最低温度 : 14℃ 风向 : 无持续风向 风力 : 小于3级 ------------------------------------------------ ------------------------------------------------ 日期 : 2026-03-12 星期 : 星期四 天气 : 多云转小雨 最高温度 : 25℃ 最低温度 : 15℃ 风向 : 无持续风向 风力 : 小于3级 ------------------------------------------------ ------------------------------------------------ 日期 : 2026-03-13 星期 : 星期五 天气 : 阵雨转小雨 最高温度 : 24℃ 最低温度 : 12℃ 风向 : 无持续风向 风力 : 小于3级 ------------------------------------------------ ------------------------------------------------ 日期 : 2026-03-14 星期 : 星期六 天气 : 小雨 最高温度 : 12℃ 最低温度 : 10℃ 风向 : 无持续风向 风力 : 小于3级 ------------------------------------------------ ================================================6.可扩展方向
增加天气图标可视化
添加空气质量指数(AQI)查询
实现多语言支持
开发图形界面版本
📝 写在最后
这个小程序其实是我闲来无事自己折腾着玩写出来的,纯粹出于兴趣和对C语言、网络编程的一点学习热情。技术上肯定还有很多不成熟的地方,代码也可能写得比较“糙”,把它分享出来,主要是想:
记录一下自己的学习过程,算是做个笔记。
和大家交流交流技术。如果你对C语言、网络请求、JSON解析这些感兴趣,或者觉得这个思路有点意思,欢迎一起探讨!
抛砖引玉。代码里肯定有不少可以改进、优化的地方,或者有更好的实现方式。非常欢迎大家提出宝贵的意见、建议或者批评! 相互学习,共同进步嘛。
所以,这完全是一个供学习交流的小玩意儿。如果你觉得它还有点用,或者能给你带来一点启发,那我就很开心了!当然,也欢迎你基于这个思路去折腾出更酷的东西!
欢迎交流,欢迎指正! 😊
