day10 20240506 耗时:59min
day11 20240507 耗时:106min
课程链接地址
先去看一遍教程 扫一遍,不用完全一行行读
ctrl+shift+I调出来网页调试台——network——img
过程:
头部字段名有好多好多好多
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;
}
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;
}
查pdf——客户端请求消息
1.\r\n
是两个特殊字符的组合,表示回车(Carriage Return)和换行(Line Feed),常用于文本文件中的换行符。它们在不同的操作系统中有不同的使用方式:
\n
作为换行符。\r\n
作为换行符。\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是空的
}
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);
}
还行,调一下, 一个个调就行,有点耐心
gcc -o httprequest httprequest.c
./httprequest www.baidu.com /
/代表resource 首页所以只有/
没有东西 只有response:, 暂时没调出来
正常应该接受到源码网页右击view page source 或者CTRL+U