16.域名解析与http服务器实现原理
文章目录
- 16.域名解析与http服务器实现原理
- gethostbyname 函数
- HTTP的操作过程
- 实现http
- home.html
- http-head.txt
- server.c
gethostbyname 函数
原型:
#include <netdb.h> struct hostent *gethostbyname(const char *hostname);
功能:获取主机名对应的IP地址
参数:
hostname
:要查询的主机名。返回值:
- 成功时,返回一个指向
hostent
结构的指针。- 失败时,返回NULL。
注意:使用完该函数后记得使用
endhostent
函数进行销毁,清理缓冲区。
struct hostent
:
struct hostent {char *h_name; /* 官方域名 */char **h_aliases; /* 别名*/int h_addrtype; /* 地址族(地址类型) */int h_length; /* 地址长度 */ char **h_addr_list; /* 地址列表 */ } #define h_addr h_addr_list[0] /* 实现向后兼容性 */
h_name
:主机的正式名称
h_aliases
:主机的备用名称数组,以 NULL 结尾指针
h_addrtype
:地址类型;(AF_INET或AF_INET6)
h_length
:地址的长度(以字节为单位)
h_addr_list
:指向主机网络地址的指针数组(按网络字节顺序),由 NULL 指针终止
h_addr h_addr_list
:中的第一个地址,以实现向后兼容性示例:
#include <stdio.h> #include <netdb.h> #include <sys/socket.h> #include <stdlib.h> #include <arpa/inet.h>int main(int argc, char *argv[]) {int i; /*检查参数*/if(argc < 2){printf("%s <host name>\n", argv[0]);exit(0);} /*将域名赋值给结构体*/struct hostent *host = gethostbyname(argv[1]);/*打印结构体的内容*/for(i = 0; host->h_aliases[i] != NULL; i++){printf("%s\n", host->h_aliases[i]);}printf("Address type:%s\n",host->h_addrtype == AF_INET ? "AF_INET":"AF_INET6");for(i = 0; host->h_addr_list[i] != NULL; i++){printf("IP address %d:%s\n", i, inet_ntoa(*(struct in_addr *)host->h_addr_list[i]));}endhostent();//关闭主机信息文件return 0; }
运行程序:
./test www.baidu.com
HTTP的操作过程
- DNS解析:当用户在浏览器中输入URL或点击链接时,浏览器首先需要将域名转换为对应的IP地址。这一过程称为DNS解析。浏览器会向DNS服务器查询该域名对应的IP地址。
- 建立TCP连接:获取到IP地址后,浏览器会与服务器建立TCP连接,这一过程涉及到TCP协议的三次握手。这是为了确保数据能够准确、有序地在网络中传输。
- 封装HTTP请求数据包:一旦TCP连接建立成功,浏览器会封装一个HTTP请求数据包,其中包含了请求行、请求头和请求体等信息。
- 发送HTTP请求:随后,浏览器通过已经建立的TCP连接,向服务器发送HTTP请求。这个请求可能包含对网页文档的请求,或是对图片、CSS、JavaScript等资源的请求。
- 服务器处理请求:服务器接收到HTTP请求后,会根据请求的内容进行处理。这包括读取请求的资源、执行相应的脚本等操作。
- 服务器返回响应:处理完请求后,服务器会返回一个HTTP响应,其中包括了状态码、响应头和响应体等信息。状态码告诉客户端请求是否成功,或者遇到了何种错误。
- 浏览器解析HTML:收到响应后,浏览器开始解析HTML代码,并构建DOM树。同时,浏览器会根据HTML中的标签和属性,请求必要的资源文件,如CSS样式表、JavaScript脚本以及图片等。
- 浏览器渲染页面:在取得并解析所有资源文件后,浏览器开始渲染页面。这个过程包括计算每个元素的位置和样式,最终将页面呈现出来。
- 关闭TCP连接:一旦服务器发送了请求的数据,通常会主动关闭TCP连接。但在某些情况下,如果响应头信息中包含
Connection:keep-alive
,则连接仍然保持打开状态,以便后续在同一连接上发送更多请求
实现http
home.html
<html>
<head>
<title>server name</title>
<meta charset="utf-8">
</head>
<body>
<body>
<h1>文档标题</h1>
<p>Hello World!</p>
</body>
</body>
</html>
http-head.txt
HTTP/1.1 200 OK
Content-Type: text/html
Connection: close
server.c
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <strings.h>#define PORT 80
#define BACKLOG 5
#define HTTPFILE "http-head.txt"
#define HTMLFILE "home.html"int ClientHandle(int newfd);int main(int argc, char *argv[])
{int fd, newfd;struct sockaddr_in addr;/*创建套接字*/fd = socket(AF_INET, SOCK_STREAM, 0);if(fd < 0){perror("socket");exit(0);}int opt = 1;if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &opt, sizeof(opt) ))perror("setsockopt");addr.sin_family = AF_INET;addr.sin_port = htons(PORT);addr.sin_addr.s_addr = 0;/*绑定通信结构体*/if(bind(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){perror("bind");exit(0);}/*设置套接字为监听模式*/if(listen(fd, BACKLOG) == -1){perror("listen");exit(0);}/*接受客户端的连接请求,生成新的用于和客户端通信的套接字*/newfd = accept(fd, NULL, NULL);if(newfd < 0){perror("accept");exit(0);}ClientHandle(newfd);close(fd);return 0;
}int ClientHandle(int newfd){int file_fd = -1;char buf[BUFSIZ] = {};int ret;
/*接收数据*/do {ret = recv(newfd, buf, BUFSIZ, 0);}while(ret < 0 && errno == EINTR);//如果接收到的数据个数小于0,或者错误为非阻塞时未收到数据的标志EINTRif(ret < 0){perror("recv");exit(0);}else if(ret == 0){//客户端主动结束close(newfd);return 0;}else{printf("=====================================\n");printf("%s", buf);fflush(stdout);//刷新缓冲区}/*读取文件HTTPFILE的内容并发送*/bzero(buf, ret);file_fd = open(HTTPFILE, O_RDONLY);if(file_fd < 0){perror("open");exit(0);}ret = read(file_fd, buf, BUFSIZ);printf("%s\n", buf);send(newfd, buf, ret, 0);close(file_fd);/*读取文件HTMLFILE的内容并发送*/bzero(buf, ret);file_fd = open(HTMLFILE, O_RDONLY);if(file_fd < 0){perror("open");exit(0);}ret = read(file_fd, buf, BUFSIZ);printf("%s\n", buf);send(newfd, buf, ret, 0);close(file_fd);close(newfd);return 0;
}
-
查看ip地址
ifconfig
-
运行程序
sudo ./server
-
打开浏览器输入ip地址