深入理解计算机系统(第三版)第十一章重要内容摘要
11.1客户端-服务器编程模型
每个网络应用都是基于客户端-服务器模型的。一个应用是由一个服务器进程和一个或者多个客户端进程组成的。


客户端和服务器是进程,而不是常提到的机器或者主机。
11.2网络
对主机而言,网络只是又一种I/O设备,是数据源和数据接收方。


11.3全球IP因特网

因特网的客户端和服务器混合使用套接字接口函数和Unix I/O函数来进行通信

13.3.1 IP地址

TCP/IP为任意整数数据项定义了统一的网络字节顺序(network byte order)(大端字节顺序),例如IP地址,它放在包头中跨过网络被携带。在IP地址结构中存放的地址总是以(大端法)网络字节顺序存放的,即使主机字节顺序(host byte order)是小端法。


11.3.2因特网域名
因特网也定义了一组更加人性化的域名(domain name),以及一种将域名隐射到IP地址的机制。域名是一串用句点分隔的单词(字母、数字和破折号),例如whaleshark.ics.cs.cmu.edu。


因特网定义了域名集合和IP地址集合之间的映射。这个映射是通过分布世界范围内的数据库(称为DNS(domain Name System,域名系统))来维护的。
我们可以用Linux的NSLOOKUP程序来探究DNS映射的一些属性,这个程序能展示与某个IP地址对应的域名。
11.3.3 因特网连接

## 11.4套接字接口

11.4.1 套接字地址结构

11.4.2 socket函数

11.4.3connect函数

11.4.4bind函数


11.4.5listen函数

11.4.6accept函数


11.4.7主机和服务的转换
1.getaddrinfo函数







2.getnameinfo


11.4.8套接字接口的辅助函数
1.open_clientfd函数


/* $begin open_clientfd */
int open_clientfd(char *hostname, char *port) {
int clientfd, rc;
struct addrinfo hints, *listp, *p;
/* Get a list of potential server addresses */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM; /* Open a connection */
hints.ai_flags = AI_NUMERICSERV; /* ... using a numeric port arg. */
hints.ai_flags |= AI_ADDRCONFIG; /* Recommended for connections */
if ((rc = getaddrinfo(hostname, port, &hints, &listp)) != 0) {
fprintf(stderr, "getaddrinfo failed (%s:%s): %s\n", hostname, port, gai_strerror(rc));
return -2;
}
/* Walk the list for one that we can successfully connect to */
for (p = listp; p; p = p->ai_next) {
/* Create a socket descriptor */
if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
continue; /* Socket failed, try the next */
/* Connect to the server */
if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1)
break; /* Success */
if (close(clientfd) < 0) { /* Connect failed, try another */ //line:netp:openclientfd:closefd
fprintf(stderr, "open_clientfd: close failed: %s\n", strerror(errno));
return -1;
}
}
/* Clean up */
freeaddrinfo(listp);
if (!p) /* All connects failed */
return -1;
else /* The last connect succeeded */
return clientfd;
}
/* $end open_clientfd */</pre>

2.open_listenfd函数
通过调用open_listenfd函数,服务器创建一个监听描述符,准备好接收连接请求。


/* $begin open_listenfd */
int open_listenfd(char *port)
{
struct addrinfo hints, *listp, *p;
int listenfd, rc, optval=1;
/* Get a list of potential server addresses */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM; /* Accept connections */
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* ... on any IP address */
hints.ai_flags |= AI_NUMERICSERV; /* ... using port number */
if ((rc = getaddrinfo(NULL, port, &hints, &listp)) != 0) {
fprintf(stderr, "getaddrinfo failed (port %s): %s\n", port, gai_strerror(rc));
return -2;
}
/* Walk the list for one that we can bind to */
for (p = listp; p; p = p->ai_next) {
/* Create a socket descriptor */
if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
continue; /* Socket failed, try the next */
/* Eliminates "Address already in use" error from bind */
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, //line:netp:csapp:setsockopt
(const void *)&optval , sizeof(int));
/* Bind the descriptor to the address */
if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
break; /* Success */
if (close(listenfd) < 0) { /* Bind failed, try the next */
fprintf(stderr, "open_listenfd close failed: %s\n", strerror(errno));
return -1;
}
}
/* Clean up */
freeaddrinfo(listp);
if (!p) /* No address worked */
return -1;
/* Make it a listening socket ready to accept connection requests */
if (listen(listenfd, LISTENQ) < 0) {
close(listenfd);
return -1;
}
return listenfd;
}/* $end open_listenfd */
最后我们调用listen函数,将listenfd转换为一个监听描述符,并返回给调用者。如果listen失败,我们要小心地避免内存泄漏,在返回前关闭描述符。
11.4.9echo客户端和服务器的示例
11.5Web服务器
11.5.1Web基础
Web客户端和服务端之间的交互用的是一个基于文本地应用级协议,叫做HTTP
Web内容可以用一种叫做HTML(Hypertext Markup Language,超文本标记语言)的语言来编写。
11.5.2Web内容




11.5.3HTTP事务

1.HTTP请求



2.HTTP响应



11.5.4服务动态内容
1.客户端如何将程序参数传递给服务器


2.服务器如何将参数传递给子进程

3.服务器如何将其他信息传递给子进程

4.子进程将它的输出发送到哪里
一个CGI程序将它的动态内容发送到标准输出。在子进程加载并运行CGI程序之前。它使用Linux dup2函数将标准输出重定向到和客户端相关联地已连接描述符。因此,任何CGI程序写到标准输出的东西都会直接到达客户端。
注意,因为父进程不知道子进程生成的内容的类型或大小,所以子进程就要负责生成Content_type和Content-length响应报头,以及终止报头的空行。
11.6综合:TINY Web服务器


浙公网安备 33010602011771号