当前位置: 首页 > ops >正文

Day36 TCP客户端编程 HTTP协议解析 获取实时天气信息

day36 TCP客户端编程 HTTP协议解析 获取实时天气信息


一、项目目标

使用 Wireshark 抓包工具 捕获访问 www.nowapi.com(实际为 api.k780.com)获取实时天气数据的 HTTP 报文,
基于抓包结果,编写一个 TCP 客户端程序(C语言),实现以下功能:

  1. 与远程服务器建立 TCP 连接;
  2. 发送符合规范的 HTTP 请求报文;
  3. 接收服务器响应;
  4. 解析 JSON 格式的天气数据;
  5. 提取关键字段并格式化输出为指定样式。

最终输出示例如下:

2025-09-05:星期五:台北:31℃/25℃:晴转多云

二、前置知识准备

1. 网络通信流程(TCP + HTTP)

  • 使用 TCP 协议连接目标服务器(IP + 端口)
  • 手动构造 HTTP 请求报文(请求行 + 请求头)
  • 发送请求后接收响应(包含状态行、响应头、响应体)
  • 从响应体中提取 JSON 数据并解析

2. 关键函数说明

函数作用
socket()创建套接字
connect()建立 TCP 连接
send()发送数据
recv()接收数据
close()关闭连接
strstr()查找子字符串
strchr()查找字符首次出现位置
inet_addr()将点分十进制 IP 转换为网络字节序
htons()主机字节序转网络字节序(用于端口)

三、API 接口信息分析

目标网址

http://api.k780.com/?app=weather.today&cityNm=台北&appkey=77384&sign=5ac63f91d88ad9c5e08f6e513552b3f1&format=json

Remote Address(远程地址)

8.129.233.227:80

说明:该服务运行在标准 HTTP 端口 80 上,使用明文传输。


四、HTTP 请求报文结构(通过 Wireshark 抓取)

以下是浏览器访问上述 URL 时发出的完整 HTTP 请求报文:

GET /?app=weather.today&cityNm=台北&appkey=77384&sign=5ac63f91d88ad9c5e08f6e513552b3f1&format=json HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: max-age=0
Connection: keep-alive
Host: api.k780.com
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36

注意:我们只需保留必要的字段即可完成请求。本程序中简化为以下关键字段。


五、API 响应示例(JSON 格式)

{"success": "1","result": {"weaid": "360","days": "2025-09-05","week": "星期五","cityno": "taibeixian","citynm": "台北","cityid": "101340101","temperature": "31℃/25℃","temperature_curr": "31℃","humidity": "67%","aqi": "77","weather": "晴转多云","weather_curr": "晴","weather_icon": "http://api.k780.com/upload/weather/d/0.gif","weather_icon1": "","wind": "无持续风向","winp": "1级","temp_high": "31","temp_low": "25","temp_curr": "31","humi_high": "0","humi_low": "0","weatid": "1","weatid1": "","windid": "0","winpid": "1","weather_iconid": "0"}
}

我们需要从中提取:

  • days: 日期
  • week: 星期
  • citynm: 城市名
  • temperature: 温度范围
  • weather: 天气描述

六、C语言客户端实现(cli.c)

严格按照原始代码逻辑整理,仅添加详细注释,不修改变量名、函数名、结构。

#include <arpa/inet.h>      // 提供IP地址转换函数(如inet_addr)
#include <fcntl.h>          // 文件控制相关函数(本程序未使用)
#include <netinet/in.h>     // 定义网络地址结构(如sockaddr_in)
#include <netinet/ip.h>     // IP协议相关定义(本程序未实际使用)
#include <stdio.h>          // 标准输入输出函数
#include <stdlib.h>         // 标准库函数
#include <string.h>         // 字符串处理函数(如strstr、strchr)
#include <sys/socket.h>     // 套接字相关函数(如socket、connect)
#include <sys/types.h>      // 基本系统数据类型
#include <time.h>           // 时间相关函数(本程序未使用)
#include <unistd.h>         // 系统调用函数(如close、send)// 类型别名定义:将struct sockaddr*简化为SA,方便后续使用
typedef struct sockaddr *(SA);// 向服务器发送HTTP请求命令
// 参数:conn为已建立连接的套接字描述符
int send_cmd(int conn)
{// 定义HTTP请求各部分的字符串数组char *args[7] = {NULL};// 请求行:GET方法 + 查询参数 + HTTP版本args[0] = "GET /?app=weather.today&cityNm=台北&appkey=77384&sign=5ac63f91d88ad9c5e08f6e513552b3f1&format=json HTTP/1.1\r\n";// Host头部:必须字段,指定主机域名args[1] = "Host: api.k780.com\r\n";// User-Agent:模拟Chrome浏览器请求args[2] = "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)\r\n";// Accept:客户端可接受的内容类型args[3] = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\n";// Accept-Language:语言偏好args[4] = "Accept-Language: zh-CN,zh;q=0.9\r\n";// Accept-Encoding:支持的内容编码(压缩)args[5] = "Accept-Encoding: gzip, deflate\r\n";// Connection:保持连接;结尾两个\r\n表示头部结束args[6] = "Connection: keep-alive\r\n\r\n";// 循环发送HTTP请求的各个部分int i = 0;for (i = 0; i < 7; i++){// send(套接字, 数据, 长度, 标志位)send(conn, args[i], strlen(args[i]), 0);}// 注意:原函数无返回值,但建议可加返回值表示成功
}int main(int argc, char **argv)
{// 创建TCP套接字:AF_INET(IPv4协议),SOCK_STREAM(TCP类型),0(默认协议)int conn = socket(AF_INET, SOCK_STREAM, 0);if (-1 == conn)  // 检查套接字创建是否失败{perror("socket");  // 输出错误信息return 1;          // 异常退出}// 定义并初始化服务器地址结构struct sockaddr_in ser;bzero(&ser, sizeof(ser));  // 将地址结构清零// 设置服务器地址信息ser.sin_family = AF_INET;                  // 使用IPv4协议ser.sin_port = htons(80);                  // 设置端口为80(HTTP默认端口),htons转换字节序ser.sin_addr.s_addr = inet_addr("8.129.233.227");  // 设置服务器IP地址为8.129.233.227(天气API服务器)// 与服务器建立TCP连接int ret = connect(conn, (SA)&ser, sizeof(ser));if (-1 == ret)  // 检查连接是否失败{perror("connect error\n");  // 输出错误信息return 1;                   // 异常退出}// 发送HTTP请求send_cmd(conn);// 接收服务器响应char buf[1024] = {0};  // 存储响应数据的缓冲区ret = recv(conn, buf, sizeof(buf), 0);  // 接收数据if (ret <= 0)  // 检查接收是否失败{perror("recv");  // 输出错误信息return 1;        // 异常退出}// 定义响应成功的标志char *flag1 = "HTTP/1.1 200 OK";     // HTTP响应状态码:成功char *flag2 = "\"success\":\"1\"";   // API返回成功标识// 检查响应是否成功(同时包含HTTP 200和API success=1)if (strstr(buf, flag1) && strstr(buf, flag2)){// 定位各个字段在响应中的位置(链式查找,基于前一个字段的位置)char *days = strstr(buf, "days");           // 查找days字段char *week = strstr(days, "week");          // 在days之后查找week字段char *citynm = strstr(week, "citynm");      // 在week之后查找citynm字段char *temperature = strstr(citynm, "temperature");  // 查找temperature字段char *weather = strstr(temperature, "weather");      // 查找weather字段char *end = NULL;  // 用于标记字段值的结束位置// 提取days字段值days += 7;  // 跳过"days":"(7个字符)end = strchr(days, '"');  // 查找结束双引号*end = '\0';  // 替换为字符串结束符// 提取week字段值week += 7;  // 跳过"week":"(7个字符)end = strchr(week, '"');*end = '\0';// 提取citynm字段值citynm += 9;  // 跳过"citynm":"(9个字符)end = strchr(citynm, '"');*end = '\0';// 提取temperature字段值temperature += 14;  // 跳过"temperature":"(14个字符)end = strchr(temperature, '"');*end = '\0';// 提取weather字段值weather += 10;  // 跳过"weather":"(10个字符)end = strchr(weather, '"');*end = '\0';// 打印解析后的天气信息(按要求格式输出)printf("%s:%s:%s:%s:%s\n", days, week, citynm, temperature, weather);}close(conn);  // 关闭套接字,释放连接资源return 0;  // 程序正常退出
}

七、程序运行理想结果

假设网络正常、API 可用,运行程序后应输出如下内容:

2025-09-05:星期五:台北:31℃/25℃:晴转多云

注:实际输出取决于 API 当前返回的数据,若城市或时间变化,输出也会相应更新。


八、关键点总结

模块要点
TCP连接使用 socket() + connect() 建立与 8.129.233.227:80 的连接
HTTP请求构造手动拼接请求行和请求头,注意 \r\n 结尾与双换行结束头部
数据接收使用 recv() 接收响应,注意缓冲区大小
响应解析使用 strstr() 定位字段,strchr() 截取值,手动解析 JSON(简易方式)
安全性未处理分包、粘包、编码压缩等问题,仅适用于简单场景
扩展建议可引入 JSON 解析库(如 cJSON)提升健壮性

九、Wireshark 抓包提示(图片注释保留)

【图片注释:使用 Wireshark 捕获访问 api.k780.com 时的 TCP 与 HTTP 数据包,观察三次握手、HTTP 请求/响应、四次挥手全过程】

  • 过滤条件可输入:ip.addr == 8.129.233.227 && tcp.port == 80
  • 观察 TCP 三次握手(SYN, SYN-ACK, ACK)
  • 查看 HTTP GET 请求内容
  • 分析服务器返回的 JSON 响应体
  • 注意 Content-Length 与实际数据长度是否一致

十、注意事项

  • 本程序为简化版,未处理 gzip 压缩内容(服务器可能返回压缩数据)
  • 若服务器启用 HTTPS(端口443),需使用 OpenSSL 库进行加密通信
  • 实际开发中建议使用 libcurl 等成熟库替代手动 TCP 编程
  • JSON 解析建议使用专业库避免错误(如字段顺序变动导致 strstr 失效)

本日所学知识点涵盖:

  • TCP 客户端编程
  • HTTP 协议原理与报文构造
  • 字符串处理(strstr, strchr
  • 结构化数据提取(简易 JSON 解析)
  • 网络调试工具使用(Wireshark)
  • 系统调用接口应用(socket, connect, send, recv, close)
http://www.xdnf.cn/news/19973.html

相关文章:

  • 如何选择适合的实验室铸铁地板和铸铁试验平板?专业人士帮助指南
  • 【开题答辩全过程】以 基于Android的点餐系统为例,包含答辩的问题和答案
  • 《sklearn机器学习——多标签排序指标》
  • Conda 使用py环境隔离
  • 新后端漏洞(上)- H2 Database Console 未授权访问
  • 高级RAG策略学习(四)——上下文窗口增强检索RAG
  • 耐达讯自动化RS485与Profinet双向奔赴,伺服驱动器连接“稳稳拿捏”
  • 第24节:3D音频与空间音效实现
  • 如何使用宝塔API批量操作Windows目录文件:从获取文件列表到删除文件的完整示例
  • 【第三方网站测试:WEB安全测试中HTTP响应头安全配置的检测的几个要点】
  • 【Web安全】命令注入与代码注入漏洞解析及安全测试指南
  • 极致效率:用 Copilot 加速你的 Android 开发
  • Linux内核网络安全序列号生成机制解析
  • 复合机器人能否更换末端执行器?
  • threejs入门学习日记
  • 分布式微服务--ZooKeeper作为分布式锁
  • Spring如何解决循环依赖:深入理解三级缓存机制
  • Android13 系统源码核心目录解析
  • css margin外边距重叠/塌陷问题
  • AI时代企业获取精准流量与实现增长的GEO新引擎
  • Android14实现Settings左右分屏显示的 代码修改
  • 智能相机还是视觉系统?一文讲透工业视觉两大选择的取舍之道
  • MCP驱动企业微信智能中枢:企业级机器人服务构建全攻略
  • 嘎嘎厉害!耐达讯自动化RS485转Profinet网关就是食品温控的“天选之子”
  • vscode连接SSH
  • 25高教社杯数模国赛【C题超高质量思路+可运行代码】第十弹
  • PostgreSQL15——DML 语句
  • jodconverter将word转pdf底层libreoffice的问题
  • 企业微信AI怎么用才高效?3大功能+5个实操场景,实测效率提升50%
  • Linux服务器暴走,用Netdata+cpolar轻松驯化