LINUX 入门 7
day10 20240506 耗时:59min
day11 20240507 耗时:106min
课程链接地址
第7章 http客户端请求
1 http项目介绍与Http协议讲解
先去看一遍教程 扫一遍,不用完全一行行读
ctrl+shift+I调出来网页调试台——network——img
过程:
- client 浏览器建立与百度服务器的tcp连接(用DNS翻译成ip)
- 在tcp 连接socket上发送http协议请求request(连接IP地址端口)
- 百度server在tcp的socket连接上返回一个http协议相应response
头部字段名有好多好多好多
2 http项目,hostname转换ip
-
struct hostent *host_entry = gethostbyname(hostname);
它接受一个字符串类型的主机名作为参数,并返回相应主机的 IP 地址。
这个函数使用了
gethostbyname
函数来获取指定主机名对应的struct hostent
结构体。该结构体包含了与主机名相关联的信息,包括 IP 地址等。但需要注意的是,
gethostbyname
函数在一些操作系统中已经被标记为过时(deprecated),推荐使用getaddrinfo
函数来替代。如果你正在开发新项目或者进行更新,建议考虑使用更现代化的方法来获取主机名对应的 IP 地址。
// 1 不像上次DNS翻译太麻烦,直接用接口gethostbyname
char *host_to_ip(const char* hostname){
struct hostent *host_entry = gethostbyname(hostname);
// 点分十进制, 14.215.177.39--->unsigned int
// inet_ntoa: unsigned int-->char * 0x121212--->"18.18.18.18"
if(host_entry) return inet_ntoa((struct in_addr*)host_entry->h_addr_list);
return NULL;
}
3 http项目tcp socket链接
TCP连接必须先建立一个socket
-
struct sockaddr_in 多年没变,接口pasca api很稳定,即使内核升级,应用没事
-
阻塞
if socket阻塞,read()时,一旦socket里没数据,整个线程挂起等io数据来
if 非阻塞,立马返回,线程不会挂起
一般选非阻塞IO
调用
fcntl(sockfd, F_SETFL, O_NONBLOCK)
的作用是将sockfd
所代表的套接字文件描述符设置为非阻塞模式。这意味着在进行读写操作时,它不会阻塞等待结果返回,而是立即返回。
// 2 连接服务器
int http_create_socket(char *ip){
// TCP连接必须先建立一个socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in sin = {0};
sin.sin_family = AF_INET;
sin.sin_port= htons(80);
sin.sin_addr = inet_addr(ip); //ip变成域名反过来
if (0 != connect(sockfd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in))) {
return -1;
}
fcntl(sockfd, F_SETFL, O_NONBLOCK); //阻塞非阻塞
return sockfd;
}
4 http项目 send http请求
查pdf——客户端请求消息
1.\r\n
是两个特殊字符的组合,表示回车(Carriage Return)和换行(Line Feed),常用于文本文件中的换行符。它们在不同的操作系统中有不同的使用方式:
- 在 Unix/Linux 系统中,通常使用
\n
作为换行符。 - 在 Windows 系统中,通常使用
\r\n
作为换行符。 - 在老式 Macintosh 系统中,通常使用
\r
作为换行符。
所以,当你需要跨平台编写代码时,在文本处理或网络传输中都建议使用 \r\n
作为换行符,这样可以保证在各种操作系统上正确解析和显示换行。
-
sprintf(buffer, "GET %s %s\r\n HOST: %s\r\n, %s\r\n\ \r\n", resource, HTTP_VERSION, hostname, CONNETION_TYPE );
%s
是一个占位符,用于替代 resource、HTTP_VERSION、hostname 和 CONNECTION_TYPE 变量的值。
3 send(sockfd,buffer, strlen(buffer), 0);
-
sockfd
是已连接套接字的文件描述符。 -
buffer
是要发送的数据缓冲区。 -
strlen(buffer)
表示要发送的数据长度(以字节为单位)。 -
0
是可选参数 flags,表示额外选项,默认设置为 0。// 3 请求http // hostname: github.com // resource /wangbojing char * http_send_request(const char *hostname, const char *resource) { char *ip = host_to_ip(hostname); //name-->ip int sockfd = http_create_socket(ip); //创建一个socket tcp连接 char buffer[BUFFER_SIZE] ={0}; sprintf(buffer, "GET %s %s\r\n HOST: %s\r\n, %s\r\n\ \r\n", resource, HTTP_VERSION, hostname, CONNETION_TYPE ); send(sockfd,buffer, strlen(buffer), 0); // 获取response, 因为非阻塞,可能还没返回所以recv是空的 }
5 http项目 select使用讲解与http response接收
select监听判断 网络IO里有没有可读数据
多次recv拼起来到result 好多网络编程看不懂查
-
fd_set fdread;, FD_ZERO(&fdread); FD_SET(sockfd, &fread);
这段代码是用于设置一个文件描述符集合,然后将 sockfd(socket 文件描述符)添加到该集合中。
首先,
fd_set fdread;
定义了一个文件描述符集合 fdread。接下来,
FD_ZERO(&fdread);
会将 fdread 集合清空,初始化为空集。最后,
FD_SET(sockfd, &fread);
将 sockfd 添加到 fdread 集合中。此操作表示将 sockfd 加入到待检查的读取文件描述符集合中。这段代码通常在使用 select() 或 epoll() 等 I/O 多路复用函数时使用,用于指定要监视的文件描述符。
-
int selection = select(sockfd+1, fread, NULL, NULL, &tv); // 可读maxfd+1, 可读集合&rset,可写集合&wset, 错误集合&eset, 多长时间遍历一次所有IO if (!selection || !FD_ISSET(sockfd, &fdread)) { break; } else{ memset(buffer, 0, BUFFER_SIZE); recv(sockfd, buffer, BUFFER_SIZE, 0); //sockfd读到buffer if(len ==0) break; //disconnect } result = realloc(result, (strlen(result) + len + 1) * sizeof(char)); strncat(result, buffer, len);
这段代码是一个基于 select() 函数的网络编程示例,用于检测套接字 sockfd 是否可读。下面是代码的解释:
int selection = select(sockfd+1, fread, NULL, NULL, &tv);
这行代码调用了 select() 函数来等待 sockfd 的可读事件。第一个参数是文件描述符最大值加 1,表示要监视的最大文件描述符数(sockfd+1)。第二个参数是可读集合,即包含 sockfd 的 fd_set 结构体指针(fread)。if (!selection || !FD_ISSET(sockfd, &fdread)) { break; }
这个条件判断语句会在 select() 返回之后进行处理。如果 select() 返回值为 0 或者 sockfd 不在可读集合中,则跳出循环。memset(buffer, 0, BUFFER_SIZE); recv(sockfd, buffer, BUFFER_SIZE, 0);
这两行代码将 buffer 数组清零,并通过 recv() 函数从 sockfd 接收数据存入 buffer 中。if (len == 0) break;
如果 len(recv() 返回的结果)等于 0,说明连接已经断开,跳出循环。result = realloc(result, (strlen(result) + len + 1) * sizeof(char)); strncat(result, buffer, len);
这两行代码用于动态扩展 result 字符串,并将接收到的数据追加到 result 中。realloc() 函数重新分配了足够大小的内存空间来容纳 result 和 buffer 的内容,然后使用 strncat() 函数将 buffer 中的数据追加到 result 的末尾。
总体来说,这段代码是一个基本的网络编程示例,使用 select() 函数实现了非阻塞式读取 sockfd 上的数据,并将接收到的数据存储在 result 字符串中。
//接上一次的函数里继续
// 3 获取response, 因为非阻塞,可能还没返回所以recv是空的
// select
fd_set fdread;,
FD_ZERO(&fdread);
FD_SET(sockfd, &fread);
struct timeval tv;
tv.tv_sec = 5; //等待秒数
tv.tv_usec = 0; //等待微秒数
char *result = malloc(sizeof(int)); //malloc的数据一定要memset0防止混乱数据,最后要free
memset(result, 0, sizeof(int));
while(1){
int selection = select(sockfd+1, fread, NULL, NULL, &tv); // 可读maxfd+1, 可读集合&rset,可写集合&wset, 错误集合&eset, 多长时间遍历一次所有IO
if (!selection || !FD_ISSET(sockfd, &fdread)) {
break;
} else{
memset(buffer, 0, BUFFER_SIZE);
recv(sockfd, buffer, BUFFER_SIZE, 0); //sockfd读到buffer
if(len ==0) break; //disconnect
}
result = realloc(result, (strlen(result) + len + 1) * sizeof(char));
strncat(result, buffer, len);
}
}
int main(int argc, char *argv[]) {
if (argc < 3) return -1;
char *response = http_send_request(argv[1], argv[2]);
printf("response : %s\n", response);
free(response);
}
6 http项目编译调试,网页请求与API接口请求
还行,调一下, 一个个调就行,有点耐心
gcc -o httprequest httprequest.c
./httprequest www.baidu.com /
/代表resource 首页所以只有/
没有东西 只有response:, 暂时没调出来
正常应该接受到源码网页右击view page source 或者CTRL+U