基于Linux的天气查询项目

目录

一、项目原理

[1. TCP 网络通信](#1. TCP 网络通信)

[2. HTTP 协议](#2. HTTP 协议)

[3. JSON 解析](#3. JSON 解析)

二、代码结构

[1. 核心函数说明](#1. 核心函数说明)

[2. 关键代码片段](#2. 关键代码片段)

[(1)TCP 连接建立](#(1)TCP 连接建立)

[(2)HTTP 请求构造(以实时天气为例)](#(2)HTTP 请求构造(以实时天气为例))

(3)未来天气温度拆分解析

三、项目步骤

4、项目结果

[1. 实时天气查询](#1. 实时天气查询)

[2. 未来天气查询](#2. 未来天气查询)

[3. 退出功能](#3. 退出功能)

五、项目分析与问题解决

[1. 核心问题及解决](#1. 核心问题及解决)

[2. 优化点](#2. 优化点)

六、完整代码


一、项目原理

1. TCP 网络通信

客户端通过socket()创建套接字,connect()连接到天气 API 服务器(地址:103.205.5.206,端口:80),通过send()发送 HTTP 请求,recv()接收服务器响应数据。

2. HTTP 协议

构造符合 HTTP/1.1 规范的 GET 请求报文,包含请求行、请求头(Host、User-Agent 等),服务器返回包含 JSON 数据的 HTTP 响应,需跳过响应头提取 JSON 正文。

3. JSON 解析

使用 cJSON 库解析 JSON 字符串:

  • cJSON_Parse()将 JSON 字符串转为 cJSON 对象;
  • cJSON_GetObjectItem()提取指定字段;
  • cJSON_ArrayForEach()遍历 JSON 数组;
  • cJSON_Delete()释放内存,避免内存泄漏。

二、代码结构

1. 核心函数说明

函数名 功能
clear_input() 清空输入缓冲区,解决 scanf 与 fgets 的输入冲突
parse_now_weather() 解析实时天气 JSON 数据,格式化输出地区、温度、湿度等信息
parse_future_weather() 解析未来天气 JSON 数据,拆分显示最低 / 最高温度
now_weather() 构造实时天气 HTTP 请求,发送并接收数据,调用解析函数
future_weather() 构造未来天气 HTTP 请求,发送并接收数据,调用解析函数
print_menu() 打印操作菜单,提供实时 / 未来天气查询、退出选项
main() 主函数,创建套接字、连接服务器、循环处理用户指令

2. 关键代码片段

(1)TCP 连接建立
cpp 复制代码
fd = socket(AF_INET,SOCK_STREAM,0); // 创建TCP套接字
struct sockaddr_in addr;
bzero(&addr,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("103.205.5.206"); // API服务器地址
addr.sin_port = htons(80); // HTTP默认端口
connect(fd,(const struct sockaddr*)&addr,sizeof(addr)); // 连接服务器
(2)HTTP 请求构造(以实时天气为例)
cpp 复制代码
sprintf(sbuf,"GET /?app=weather.realtime&weaid=%s&appkey=78726&sign=3a82de6ed4f0bfc8c8064cc51875a103&format=json HTTP/1.1\r\n",city);
char *args[8] = {sbuf, "Host: api.k780.com\r\n", ..., "Upgrade-Insecure-Requests: 1\r\n\r\n"};
for(i=0;i<8;++i) send(fd,args[i],strlen(args[i]),0); // 发送请求
(3)未来天气温度拆分解析
cpp 复制代码
const char *temp_low = cJSON_GetObjectItem(item, "temp_low") ? cJSON_GetObjectItem(item, "temp_low")->valuestring : "未知";
const char *temp_high = cJSON_GetObjectItem(item, "temp_high") ? cJSON_GetObjectItem(item, "temp_high")->valuestring : "未知";
printf("最低温度:%s℃\n", temp_low);
printf("最高温度:%s℃\n", temp_high);

三、项目步骤

  1. 环境准备 :安装 cJSON 库(sudo apt install libcjson-dev);
  2. 代码编译gcc weather.c -o weather -lcjson -lm
  3. 运行程序./weather
  4. 功能测试
    • 输入1,输入城市拼音(如 xian),查看实时天气;
    • 输入2,输入城市拼音,查看未来天气(含高低温);
    • 输入3,退出程序。

4、项目结果

1. 实时天气查询

cpp 复制代码
 weather 天气查询
1.实时天气
2.未来天气
3.退出
请输入操作指令:1
input a city:xian

========== 实时天气 ==========
地区:陕西-西安
星期:星期四
天气:多云
温度:12℃
湿度:30%
风向:东北风
AQI:56
==============================

2. 未来天气查询

cpp 复制代码
 weather 天气查询
1.实时天气
2.未来天气
3.退出
请输入操作指令:2
input a city:xian

========== 未来天气 ==========
日期:2026-03-12(星期四)
天气:多云
最低温度:8℃
最高温度:15℃
------------------------------
日期:2026-03-13(星期五)
天气:晴
最低温度:9℃
最高温度:16℃
------------------------------

3. 退出功能

cpp 复制代码
请输入操作指令:3
天气查询已退出

五、项目分析与问题解决

1. 核心问题及解决

  • 输入冲突问题 :scanf 读取指令后,输入缓冲区残留换行符,导致 fgets 读取城市名时直接为空,通过clear_input()清空缓冲区解决;
  • JSON 解析失败 :未来天气 API 返回的 JSON 结构为result->future数组,原逻辑误将result当作数组,修正为先取result对象,再遍历future数组;
  • 温度显示需求 :按要求拆分未来天气温度为最低 / 最高温,提取temp_lowtemp_high字段替换原temp字段;
  • 数据接收不完整:增大缓冲区至 8192 字节,确保能接收完整的 HTTP 响应数据。

2. 优化点

  • 增加重连错误校验,避免套接字关闭后重连失败导致程序崩溃;
  • 兼容 JSON 起始位置,通过strstr(response, "{")兜底查找 JSON 正文;
  • 字段提取时增加空指针判断,避免空指针访问导致程序崩溃。

六、完整代码

cpp 复制代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <strings.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include "cJSON.h"

int fd;
char sbuf[1024];//实时天气
char fbuf[1024];//未来天气

// 解决scanf和fgets的输入冲突
void clear_input()
{
    while (getchar() != '\n');
}

// 解析实时天气JSON(适配截图中的返回格式)
void parse_now_weather(const char *response)
{
    // 跳过HTTP响应头,定位JSON起始(找到第一个{)
    const char *json_start = strstr(response, "{\"success\"");
    if (!json_start)
	{
        json_start = strstr(response, "{"); // 兼容其他JSON起始
    }
    if (!json_start)
	{
        printf("未找到天气数据\n");
        return;
    }

    // 解析JSON
    cJSON *root = cJSON_Parse(json_start);
    if (!root)
	{
        printf("JSON解析失败: %s\n", cJSON_GetErrorPtr());
        return;
    }

    // 提取核心字段(完全适配截图中的返回结构)
    cJSON *result = cJSON_GetObjectItem(root, "result");
    if (result && cJSON_IsObject(result))
	{
        // 基础城市信息
        const char *area1 = cJSON_GetObjectItem(result, "area_1") ? cJSON_GetObjectItem(result, "area_1")->valuestring : "未知";
        const char *area2 = cJSON_GetObjectItem(result, "area_2") ? cJSON_GetObjectItem(result, "area_2")->valuestring : "未知";
        
        // 实时天气详情
        cJSON *realTime = cJSON_GetObjectItem(result, "realTime");
        if (realTime && cJSON_IsObject(realTime))
		{
            const char *week = cJSON_GetObjectItem(realTime, "week") ? cJSON_GetObjectItem(realTime, "week")->valuestring : "未知";
            const char *wtNm = cJSON_GetObjectItem(realTime, "wtNm") ? cJSON_GetObjectItem(realTime, "wtNm")->valuestring : "未知";
            const char *wtTemp = cJSON_GetObjectItem(realTime, "wtTemp") ? cJSON_GetObjectItem(realTime, "wtTemp")->valuestring : "未知";
            const char *wtHumi = cJSON_GetObjectItem(realTime, "wtHumi") ? cJSON_GetObjectItem(realTime, "wtHumi")->valuestring : "未知";
            const char *wtWindNm = cJSON_GetObjectItem(realTime, "wtWindNm") ? cJSON_GetObjectItem(realTime, "wtWindNm")->valuestring : "未知";
            const char *wtAqi = cJSON_GetObjectItem(realTime, "wtAqi") ? cJSON_GetObjectItem(realTime, "wtAqi")->valuestring : "未知";

            // 格式化输出(仅显示关键信息,隐藏冗余字段)
            printf("\n========== 实时天气 ==========\n");
            printf("地区:%s-%s\n", area1, area2);
            printf("星期:%s\n", week);
            printf("天气:%s\n", wtNm);
            printf("温度:%s℃\n", wtTemp);
            printf("湿度:%s%%\n", wtHumi);
            printf("风向:%s\n", wtWindNm);
            printf("AQI:%s\n", wtAqi);
            printf("==============================\n");
        }
    }

    cJSON_Delete(root);
}

// 解析未来天气JSON(适配API返回格式)
void parse_future_weather(const char *response)
{
    const char *json_start = strstr(response, "{\"success\"");
    if (!json_start)
	{
        json_start = strstr(response, "{");
    }
    if (!json_start)
	{
        printf("未找到天气数据\n");
        return;
    }

    cJSON *root = cJSON_Parse(json_start);
    if (!root)
	{
        printf("JSON解析失败: %s\n", cJSON_GetErrorPtr());
        return;
    }

    cJSON *result = cJSON_GetObjectItem(root, "result");
    if (result && cJSON_IsArray(result))
	{
        printf("\n========== 未来天气 ==========\n");
        cJSON *item = NULL;
        cJSON_ArrayForEach(item, result)
		{
            const char *days = cJSON_GetObjectItem(item, "days") ? cJSON_GetObjectItem(item, "days")->valuestring : "未知";
            const char *week = cJSON_GetObjectItem(item, "week") ? cJSON_GetObjectItem(item, "week")->valuestring : "未知";
            const char *weather = cJSON_GetObjectItem(item, "weather") ? cJSON_GetObjectItem(item, "weather")->valuestring : "未知";
            //const char *temp = cJSON_GetObjectItem(item, "temp") ? cJSON_GetObjectItem(item, "temp")->valuestring : "未知";

			  const char *temp_low = cJSON_GetObjectItem(item, "temp_low") ? cJSON_GetObjectItem(item, "temp_low")->valuestring : "未知";
                const char *temp_high = cJSON_GetObjectItem(item, "temp_high") ? cJSON_GetObjectItem(item, "temp_high")->valuestring : "未知";


            printf("日期:%s(%s)\n", days, week);
            printf("天气:%s\n", weather);
           // printf("温度:%s℃\n", temp);
            printf("最低温度:%s℃\n", temp_low);
            printf("最高温度:%s℃\n", temp_high);
            printf("------------------------------\n");
        }
    } else
	{
        printf("未找到未来天气数据\n");
    }

    cJSON_Delete(root);
}

// 实时天气(保留你能输入拼音的逻辑,仅替换输出为解析结果)
void now_weather()
{
	char city[256];
	printf("input a city:");
	fgets(city,sizeof(city),stdin);
	city[strlen(city)-1] = '\0';
	sprintf(sbuf,"GET /?app=weather.realtime&weaid=%s&appkey=78726&sign=3a82de6ed4f0bfc8c8064cc51875a103&format=json HTTP/1.1\r\n",city);

	char *args[8];
	args[0] = sbuf;
	args[1] = "Host: api.k780.com\r\n";
	args[2] = "User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/113.0\r\n";
	args[3] = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\n";
	args[4] = "Accept-Language: en-US,en;q=0.5\r\n";
	args[5] = "Accept-Encoding: gzip, deflate\r\n";
	args[6] = "Connection: keep-alive\r\n";
	args[7] = "Upgrade-Insecure-Requests: 1\r\n\r\n";

	int i = 0;
	for(i=0;i<8;++i)
	{
		send(fd,args[i],strlen(args[i]),0);
	}
	// 增大缓冲区,接收完整数据
	char buf[8192]={0};
	recv(fd,buf,sizeof(buf),0);
	// 替换原始打印,调用解析函数
	parse_now_weather(buf);
}

// 未来天气(同逻辑优化)
void future_weather()
{
	char city[256];
	printf("input a city:");
	fgets(city,sizeof(city),stdin);
	city[strlen(city)-1] = '\0';
	sprintf(fbuf,"GET /?app=weather.future&weaid=%s&appkey=78726&sign=3a82de6ed4f0bfc8c8064cc51875a103&format=json HTTP/1.1\r\n",city);

	char *args[8];
	args[0] = fbuf;
	args[1] = "Host: api.k780.com\r\n";
	args[2] = "User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/113.0\r\n";
	args[3] = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\n";
	args[4] = "Accept-Language: en-US,en;q=0.5\r\n";
	args[5] = "Accept-Encoding: gzip, deflate\r\n";
	args[6] = "Connection: keep-alive \r\n";
	args[7] = "Upgrade-Insecure-Requests: 1\r\n\r\n";

	int i = 0;
	for(i=0;i<8;++i)
	{
		send(fd,args[i],strlen(args[i]),0);
	}
	char buf[8192]={0};
	recv(fd,buf,sizeof(buf),0);
	parse_future_weather(buf);
}

// 打印菜单(无修改)
void print_menu()
{
	printf("\n weather 天气查询\n");
	printf("1.实时天气\n");
	printf("2.未来天气\n");
	printf("3.退出\n");
	printf("请输入操作指令:");
}

// 主函数(仅补充重连错误校验)
int main(int argc, const char *argv[])
{
	fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd < 0)
	{
		perror("socket fail");
		return -1;
	}

	struct sockaddr_in addr;
	bzero(&addr,sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("103.205.5.206");
	addr.sin_port = htons(80);
	if(connect(fd,(const struct sockaddr*)&addr,sizeof(addr)) < 0)
	{
		perror("connect fail");
		return -1;
	}
	
	int choice;
	while(1)
	{
		print_menu();
		scanf("%d",&choice);
		clear_input();
		switch (choice)
		{
		case 1:
			now_weather();
			close(fd);
			fd = socket(AF_INET,SOCK_STREAM,0);
			bzero(&addr,sizeof(addr));
			addr.sin_family = AF_INET;
			addr.sin_addr.s_addr = inet_addr("103.205.5.206");
			addr.sin_port = htons(80);
			// 补充重连校验
			if(connect(fd,(const struct sockaddr*)&addr,sizeof(addr)) < 0) {
				perror("reconnect fail");
				return -1;
			}
			break;
		case 2:
			future_weather();
			close(fd);
			fd = socket(AF_INET,SOCK_STREAM,0);
			bzero(&addr,sizeof(addr));
			addr.sin_family = AF_INET;
			addr.sin_addr.s_addr = inet_addr("103.205.5.206");
			addr.sin_port = htons(80);
			if(connect(fd,(const struct sockaddr*)&addr,sizeof(addr)) < 0) {
				perror("reconnect fail");
				return -1;
			}
			break;
		case 3:
			close(fd);
			printf("天气查询已退出\n");
			exit(0);
		default:
			printf("指令无效,请重新输入\n");
			break;
		}
	}
	return 0;
}
相关推荐
新缸中之脑2 小时前
Agent-browser浏览器自动化CLI
运维·自动化
xcs194052 小时前
AI 自动化编程 trae 项目整个调整
运维·自动化
卤炖阑尾炎2 小时前
Linux firewalld 防火墙从入门到精通:原理与配置全解析
linux·运维·php
小云数据库服务专线2 小时前
linux grep命令
linux·运维·服务器
daad7772 小时前
peerconnection
linux
李昊哲小课2 小时前
Python CSV 模块完整教程
java·服务器·python
|华|2 小时前
Nginx 核心功能
运维·nginx
自在极意功。2 小时前
nginx和docker面试题
运维·nginx·docker
木子欢儿2 小时前
从零到精通 Neovim:Ubuntu 下的终极开发利器指南
linux·运维·服务器·ubuntu