使用HTTP获取Web服务
HTTP(超文本)协议
概念
一种详细规定了浏览器和万维网服务器之间互相通信的规则,通过因特网送万维网文档的数据传送协议
简单快速:客户端向服务器请求服务时,只需要传送请求方法和路径,常用方法:GET,POST
GET,又叫明文,请求的信息是不加密的
POST,又叫密文,请求的信息是加密的
无连接:限制每次链接只处理一个请求
无状态:即如果后续处理需要前面的信息,它必须重传,这样可能每次连接传输的数据量会增大(采用多线程的方法解决)
HTTP请求头部:
\r\n 在每一行的末尾
URL:统一资源标识符
Method 方法
请求行格式:”请求方法“ URL/URI 版本 \r\n
Host:向那个服务器做请求
头部字段1: Host:www.w3.org \r\n
HTTP协议的请求方法常见的有GET、HEAD、POST、PUT等,每种请求方法的规则各不相同
URL:http: + // + web服务器 + / + 目录名 + / +... (路径)
最后没东西了,还要加 \r\n
HTTP/1.1 是主流版本,因为是长连接 ,请求后不会自动断开
持久连接
http是建立在tcp之上,所以需要利用TCP协议连接到服务器,通多getsockopt()函数解析域名
客户端必须指定Host字段: Host : 主机域名 \r\n 比如 “Host: www.baidu.com\r\n”
双方建立连接,此时可以利用HTTP协议发送请求信息,并等待服务器的响应,基于请求/响应
请求行:GET URL HTTP/1.1\r\n
请求字段:Host:api.seniverse.com\r\n
注意:因为复制原因所以反斜杠多了一个应该是 \r\n 的格式
HTTP状态编码
服务器响应的数据报文中存在状态码,用户通过状态码可以知道服务器的响应状态,在HTTP标准中有对应的状态码类型,如下:
当客户端接收到服务器的响应报文之后,可以根据响应报文中的状态码分析可能存在的问题并针对性解决。
HTTP的基本应用
Web编程客户端(获取心知天气)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#define ERR_LOG(errmsg) \
do \
{ \
perror(errmsg); \
printf("%s,%s,%d\n", __FILE__, __func__, __LINE__); \
exit(1); \
} while (0)
#define N 1024
#define API_KEY "your_api_key"
#define LOCATION "your_location"
#define GET_URL(key, location) "GET https://api.seniverse.com/v3/weather/now.json?key=" key "&location=" location "&language=zh-Hans&unit=c "
int main(int argc, char const *argv[])
{
// 判断参数个数是否正确
if (argc != 3)
{
printf("Usage: %s <port> <ip>\n", argv[0]);
exit(1);
}
printf("port: %s\n", argv[1]);
// 可以添加域名解析
// 创建套接字
int cli_fd = socket(AF_INET, SOCK_STREAM, 0);
if (cli_fd == -1)
{
ERR_LOG("socket");
return -1;
}
// 连接服务器
struct sockaddr_in ser_inf;
memset(&ser_inf, 0, sizeof(ser_inf));
ser_inf.sin_family = AF_INET; // 协议族
ser_inf.sin_port = htons(atoi(argv[1])); // 服务器端口号
ser_inf.sin_addr.s_addr = inet_addr(argv[2]); // 服务器IP地址
if (connect(cli_fd, (struct sockaddr *)&ser_inf, sizeof(ser_inf)) == -1)
{
ERR_LOG("connect");
return -1;
}
// 创建文件描述符集合
fd_set fdset;
// 清空文件描述符集合
FD_ZERO(&fdset);
// 把要添加监听的文件描述符添加到集合里面
FD_SET(cli_fd, &fdset); // 接收消息你要把那个文件描述符添加进去进行监听
// 用于存储HTTP请求内容:请求行+请求头+\r\n+请求包体(可选)
char msg[N];
sprintf(msg, GET_URL(API_KEY, LOCATION) "HTTP/1.1"
"\r\n"
"Host: api.seniverse.com\r\n"
"\r\n");
// 开始实时轮询监听
while (1)
{
// 清空文件描述符集合
FD_ZERO(&fdset);
// 把要添加监听的文件描述符添加到集合里面
FD_SET(cli_fd, &fdset); // 接收消息你要把那个文件描述符添加进去进行监听
// 监控的文件描述符范围是从 0 到 nfds-1。通常设置为所监控的文件描述符中最大值加 1。
if (select(cli_fd + 1, &fdset, NULL, NULL, NULL) == -1)
{
ERR_LOG("select");
return -1;
}
if (FD_ISSET(cli_fd, &fdset))
{
bzero(msg, N);
recv(cli_fd, msg, N, 0);
printf("%s\n", msg); // 响应头部信息
bzero(msg, N);
recv(cli_fd, msg, N, 0);
printf("%s\n", msg); // 响应包体信息
}
}
// 关闭套接字
close(cli_fd);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· Qt个人项目总结 —— MySQL数据库查询与断言