Linux:网络编程-基于HTTP协议的天气预报查询系统开发详解

目录

1.系统架构与核心功能

2.关键技术实现

2.1网络通信

2.2JSON数据解析

3.完整工作流程

4.技术亮点

5.成果展示

5.1代码

5.2使用示例

6.扩展方向


引言

在嵌入式开发或网络编程学习中,通过Socket编程实现与Web API的交互是一项基础而重要的技能。本文介绍了我开发的一个基于HTTP协议的天气预报查询系统。该系统通过以下流程获取数据:

  1. 建立TCP连接至远程服务器(nowapi提供的API接口)

  2. 发送符合规范的HTTP请求

  3. 接收并解析JSON格式的响应数据

  4. 提取并展示指定城市的实时天气及未来天气预报

下文将详细解析系统实现的技术细节,包括Socket通信机制、HTTP协议处理、JSON数据解析等核心模块的实现方案。

1.系统架构与核心功能

该系统采用模块化设计,主要包含以下功能模块:

  1. 网络通信模块:建立TCP连接并发送HTTP请求

  2. 数据解析模块:处理JSON格式的天气数据

  3. 数据展示模块:格式化输出天气信息

程序通过Weather结构体存储天气数据:

cpp 复制代码
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请求

cpp 复制代码
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数据

  • 分别处理当天和未来天气数据

  • 错误处理机制确保数据有效性

cpp 复制代码
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用户交互与展示

  • 终端交互式输入城市名称

  • 格式化输出当天和未来天气信息

  • 清晰的数据分隔线提升可读性

cpp 复制代码
int ShowToday() {
    printf("------------------------------------------------\n");
    printf("城市    : %s\n",Today.city);
    printf("日期    : %s\n",Today.date);
    // ...其他天气信息输出
    printf("------------------------------------------------\n");
}

3.完整工作流程

  1. 用户输入目标城市名称

  2. 程序创建TCP连接并发送天气请求:

    • 当天天气:/?app=weather.today&weaid=城市名...

    • 未来天气:/?app=weather.future&weaid=城市名...

  3. 接收并解析JSON格式的天气数据

  4. 格式化输出天气信息:

    • 当天天气详情

    • 未来7天天气预报

4.技术亮点

  1. 高效的网络通信:直接使用socket进行TCP通信,避免依赖外部库

  2. 健壮的错误处理:检测API返回的success字段,无效数据时优雅退出

  3. 内存管理:使用cJSON_Delete释放解析后的JSON对象

  4. 数据封装:Weather结构体清晰组织天气数据

5.成果展示

5.1代码

cpp 复制代码
#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使用示例

cpp 复制代码
//
./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.可扩展方向

  1. 增加天气图标可视化

  2. 添加空气质量指数(AQI)查询

  3. 实现多语言支持

  4. 开发图形界面版本

📝 写在最后

这个小程序其实是我闲来无事自己折腾着玩写出来的,纯粹出于兴趣和对C语言、网络编程的一点学习热情。技术上肯定还有很多不成熟的地方,代码也可能写得比较"糙",把它分享出来,主要是想:

  1. 记录一下自己的学习过程,算是做个笔记。

  2. 和大家交流交流技术。如果你对C语言、网络请求、JSON解析这些感兴趣,或者觉得这个思路有点意思,欢迎一起探讨!

  3. 抛砖引玉。代码里肯定有不少可以改进、优化的地方,或者有更好的实现方式。非常欢迎大家提出宝贵的意见、建议或者批评!​ 相互学习,共同进步嘛。

所以,这完全是一个供学习交流的小玩意儿。如果你觉得它还有点用,或者能给你带来一点启发,那我就很开心了!当然,也欢迎你基于这个思路去折腾出更酷的东西!

欢迎交流,欢迎指正!​ 😊

相关推荐
良许Linux2 小时前
汽车电子技术和汽车网络
网络·单片机·嵌入式硬件·汽车·嵌入式·制造
艾莉丝努力练剑2 小时前
【Linux进程间通信:共享内存】为什么共享内存的 key 值由用户设置
java·linux·运维·服务器·开发语言·数据库·mysql
微露清风2 小时前
系统性学习Linux-第四讲-进程控制
linux·服务器·学习
不脱发的程序猿2 小时前
嵌入式Linux:阻塞式I/O与非阻塞式I/O
linux·服务器·单片机·嵌入式硬件·嵌入式
Qt程序员2 小时前
基于 C++ 实现自定义字符串 string 类
linux·c++·容器·指针·内存管理·运算符重载
凉拌菜3 小时前
手术摄像系统的视频延迟是如何产生的?从采集到网络传输的技术解析
网络·音视频·医疗视频·4k视频·术野摄像机
从零点3 小时前
认识Linux和mpu开发板之间的联系
linux
牛十二3 小时前
Ubuntu 虚拟机安装完全免费的网易有道龙虾实战流程
linux·运维·ubuntu
yangyanping201083 小时前
Linux学习三之 清空 nohup.out 文件
linux·chrome·学习