目录
1 域名解析
2 如何实现万维网服务器?
2.1 HTTP 的操作过程
2.2 万维网服务器实现
1 域名解析
域名解析gethostbyname函数
主机结构在 <netdb.h> 中定义如下:
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;
}
实现效果
2 如何实现万维网服务器?
2.1 HTTP 的操作过程
HTTP 规定:在 HTTP 客户与 HTTP 服务器之间的每次交互,都由一个 ASCII 码串构成的请求和一个类似的通用互联网扩充,即“类MIME (MIME-like)”的响应组成。
HTTP 报文通常都使用 TCP 连接传送。
http连接过程
html解释型语言
<html>
<head><title>home</title></head>
<body>
<center><h1>Hello World!</h1></center>
<hr><center>nginx</center>
</body>
</html>
打开home.html(firefox ./home.html)
2.2 万维网服务器实现
nc命令实现步骤
- 打开终端,nc <本机ip> 80
- 打开游览器访问本机ip
- 在终端输入以下内容
HTTP/1.1 200 OK
Content-Type: text/html<html>
<head><title>home</title></head>
<body>
<center><h1>Hello World!</h1></center>
<hr><center>nginx</center>
</body>
</html>
代码实现步骤
- 准备好http头文件
- 准备好html文件
- 创建TCP socket
- 实现client hanlde功能
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
服务代码
#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并且信号中断if(ret < 0){perror("recv");exit(0);}else if(ret == 0){close(newfd);return 0;}else{printf("=====================================\n");printf("%s", buf);fflush(stdout);}bzero(buf, ret);file_fd = open(HTTPFILE, O_RDONLY); //http文件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);bzero(buf, ret);file_fd = open(HTMLFILE, O_RDONLY); //html文件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;
}
3 练习
1.模拟实现http服务器,并使用浏览器访问。提交浏览器访问你http服务器的截图