16.域名解析与http服务器实现原理
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,或者错误为非阻塞时未收到数据的标志EINTR
if(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地址