2017-2018-1 20155332 《信息安全系统设计基础》第13周学习总结
2017-2018-1 20155332 《信息安全系统设计基础》第13周学习总结
学习目标
找出全书你认为最重要的一章,深入重新学习一下,要求(期末占10分):
完成这一章所有习题
详细总结本章要点
给你的结对学习搭档讲解你的总结并获取反馈
本章知识点总结
我们需要理解基本的客户端-服务端编程模型,以及如何编写使用因特网提供的服务的客户端-服务端程序。
最后,我们将把所有这些概念结合起来,开发一个小的但功能齐全的Web服务器,能够为真实的Web浏览器提供静态的和动态的文本和图形内容。
11.1 客户端-- 服务器模型
-
每个网络应用程序都是基于客户端 - 服务器模型的,
采用这种模型,一个应用是由一个服务器进程 和一个或多个客户端进程组成。 -
服务器管理某种资源,并且通过操作这种资源为它的客户端提供某种服务。
- WEB服务器,代表客户端检索,执行磁盘内容。
- FTP服务器,为客户端进行存储和检索。
- 电子邮件服务器,为客户端进行读和更新。
- 客户端-服务器模型中的基本操作是事务(transaction).
-
一个客户端-服务器事务由四步组成
- 客户端需要服务的时候,向服务器发送请求,发送一个事务。
- 服务器收到请求后,解释它,并以适当方式操作它的资源。
- 服务器给客户端发送一个响应,并等待下一个请求。
- 客户端收到响应并处理它。
11.2 网络
客户端
和服务端
通常运行在不同的主机上,并且通过计算机网络
的硬件和软件资源来通信。
-
对于一个主机而言,
网络
只是又一种I/O
设备,作为数据源和数据接收方。 -
对于物理上而言,网络是一个按照地理远近组成的层次系统。
- 最低层是LAN(Local Area Network,局域网):在一个建筑或校园范围内。
- 今为止,最流行的LAN技术是以太网(Ethernet),由Xerox PARC公司在20世纪70年代中期提出。以太网被证明是适应力极强的,从3 MB/s到10 GB/s
-
一个以太网段(Ethernet segment)包括一些电缆(通常是双绞线)和一个叫做集线器的小盒子.
- 一台主机可以发送一段位,称为帧(frame),到这个网段内其他任何主机。
每个帧包括一些固定数量的头部(header)位
用于表示此帧的源,和目的地址以及此帧的长度。
此后就是数据位的有效载荷。
每个主机适配器都能看到这个帧,但是只有目的主机实际读取它。
- 一台主机可以发送一段位,称为帧(frame),到这个网段内其他任何主机。
扩展介绍以太网
每个以太网适配器(网卡)都有一个全球唯一的48位地址,它存储在这个适配器的ROM上(MAC)。
-
一台主机可以发送一段位,称为帧(frame),到这个网段内其他任何主机。
每个帧包括- 一些固定数量的头部(header)位
- 用于表示此帧的源,和目的地址以及此帧的长度。
-
每个主机适配器都能看到这个帧,但是只有目的主机实际读取它。
使用一些电缆和叫做网桥(bridge)的小盒子,多个以太网段可以连接称较大的局域网,称为桥接以太网(bridged Ethernet)
- 一些电缆连接网桥与网桥,或者 网桥与集线器。
这些电缆的带宽可以是不同的。
在层次的更高级别,多个不兼容的局域网可以通过叫做路由器(router)的特殊计算机连接起来,组成一个internet(互联网络)
Internet和internet
我们总是用小写字母的internet表示一般概念,大写的Internet表示一种具体实现,如全球IP因特网。
WAN(Wide-Area Network,广域网)
互联网至关重要的特性是:
它能由采用完全不同和不兼容技术的各种局域网和广域网组成。
Q:如何能够让某台源主机跨过所有这些不兼容的网络发送数据位到另一台目的主机呢?
A:解决办法是一层运行在每台主机和路由器上的协议软件,消除不同网络之间的差异。
这个软件实现一种协议:控制主机和路由器如何协调工作来实现数据传输。
必须提供两种基本能力:
命名机制
每台主机会被分配至少一个互联网地址(internet address),这个地址唯一标识了这台主机。
传送机制
协议通过定义一种把数据位捆扎成不连续的片(称为包)的方式。
一个包是由包头和有效载荷组成的。
包头
包的大小
源主机和目的主机地址
有效载荷包括从源主机发出的数据位.
11.3 全球IP 因特网
-
每台因特网主机都运行实现TCP/IP协议 (Transmission Control Protocol/Intelnet Protocol,传输控制协议/互联网络协议)的软件,几乎所有计算机系统都支持这个协议
-
TCP/IP协议实际上一个协议族,每一个协议提供不同的功能。
-
从程序员的角度,我们可以把因特网看作世界范围内主机的集合,满足一下特性。
- 主机集合被映射为一组32位的IP地址。
- 这组IP地址可以被映射为一组称为因特网域名(Internet domain name)的标示符。
- 因特网主机上的进程能够通过连接和任何其他主机上的进程通信。
-
一个IP地址就是一个32位无符号整数。网络程序将IP地址存放在一个IP地址结构中。
-
主机字节序,和网络字节序,之间可以使用
inet_aton
和inet_ntoa
函数来实现两者之间互相转换。 -
因特网域名
叶子结点反向到根的路径就是域名。
层次结构第一层 : 未命名的根结点
层次结构第二层 : 一级域名(first-level domain name)
由非盈利组织ICANN(Internet Corporation for Assigned Names and Numbers,因特尔分配名字数字协会)定义。
常见的一级域名:com,edu,gov,org和net。
层次结构第三层: 二级域名(second-level)
例如:cmu.edu。
- 这些域名是由ICANN的各个授权代理按照先到先服务的基础分配的。
11.4 套接字接口
套接字接口(socket interface)是一组函数,他们和Unix I/O函数结合起来,用以创建网络应用。
给出一个典型的客户端-服务器事务的上下文中套接字接口概述,以此导向。
- 从Unix内核角度来看,一个套接字就是通信的一个端点。
- 从Unix程序来看,套接字就是一个有相应描述符的打开文件。
sin_family成员是AF_INET,ipv4还是ipv6。
sin_port成员是一个16位的端口号。
sin_addr成员就是一个32位的IP地址。
IP地址和端口号总是以网络字节顺序(大端法)存放的。
sockaddr_in给程序员操作的,sockaddr交由套接字函数使用的,两者可以直接强制转换。
- socket函数
- connect函数
- open_clientfd函数
- bind函数
- listen函数(主动套接字->监听套接字)
- open_listenfd函数
- accept函数
书上习题
11.7
在get_filetype函数里面添加:
else if(strstr(filename, ".mpg") || strstr(filename, ".mp4"))
strcpy(filetype, "video/mpg");
11.8
结束子进程之前,我们不可以关闭已连接描述符。这就导致我们还是得让serve_dynamic或者是doit函数或者main函数中要等待子进程结束。迭代服务器的设计让这个问题变得比较无聊。
在main函数之前加入代码:
int chdEnded ;
#include <signal.h>
void child_signal(int sig)
{
pid_t pid;
while((pid = waitpid(-1, NULL, WNOHANG)) > 0)
;
chdEnded = 1;
}
在main函数中添加语句 signal(SIGCHILD, child_handle);
每次accept之前,让chdEnded = 0;
并且在doit()中的serve_dynamic之后添加:
while(!chdEnded) pause();//or do sth
删掉serve_dynamic里的wait(NULL);
11.9
serve_static中的存储器映射语句改为:
srcfd = open(filename, O_RDONLY, 0);
srcp = (char)malloc(sizeof(char)filesize);
rio_readn(srcfd, srcp, filesize);
close(srcfd);
rio_writen(fd, srcp, filesize);
free(srcp);
11.10
HTML文件:
<html>
<body>
<form name="input" action="cgi-bin/adder" method="get">
Num1: <input type="text" name="num1"/> <br/>
Num2: <input type="text" name="num2"/> <br/>
<input type="submit" value="Submit"/>
</form>
</body>
</html>
因为提交的表单里面有参数名字(num1=x&num2=y),所以要修改相应的adder.c:
int parseNum(char *s)
{
int i = strlen(s) - 1;
while(i>0 && s[i-1]>='0'&&s[i-1]<='9' )
i--;
return atoi(s+i);
}
int main(void) {
char *buf, *p;
char arg1[MAXLINE], arg2[MAXLINE], content[MAXLINE];
int n1=0, n2=0;
/* Extract the two arguments */
if ((buf = getenv("QUERY_STRING")) != NULL) {
p = strchr(buf, '&');
*p = 0;
strcpy(arg1, buf);
strcpy(arg2, p+1);
n1 = parseNum(arg1);
n2 = parseNum(arg2);
}
/* Make the response body */
sprintf(content, "Welcome to add.com: ");
sprintf(content, "%sTHE Internet addition portal.\r\n<p>", content);
sprintf(content, "%sThe answer is: %d + %d = %d\r\n<p>",
content, n1, n2, n1 + n2);
sprintf(content, "%sThanks for visiting!\r\n", content);
/* Generate the HTTP response */
printf("Content-length: %d\r\n", (int)strlen(content));
printf("Content-type: text/html\r\n\r\n");
printf("%s", content);
fflush(stdout);
exit(0);
}
11.11
想到的办法就是定义一个变量rmtd,表示请求的方法。
在client_error,serve_static和serve_dynamic中添加一个参数mtd(改的地方也比较多),表示方法。如果mtd为HEAD,就只打印头部。
11.12
还是使用rmtd表示方法。
这里需要改的还有读取报头。
用POST方法,表单的参数是在报头之后。
报头中有一项是Content-Length,读取出来,在报头读完之后,接着读取Content-Length个字节(注意最后添0),这些字节就是form用post方法提交的数据。
主要修改的就是doit方法和read_request方法。
下面的程序只能针对参数为文本的情况,且参数总长度最大不超过MAXLINE。
#define M_GET 0
#define M_POST 1
#define M_HEAD 2
#define M_NONE (-1)
void doit(int fd)
{
int is_static;
int rmtd = 0;
struct stat sbuf;
char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
char filename[MAXLINE], cgiargs[MAXLINE];
rio_t rio;
/*for post*/
int contentLen;
char post_content[MAXLINE];
/* Read request line and headers */
rio_readinitb(&rio, fd);
rio_readlineb(&rio, buf, MAXLINE);
sscanf(buf, "%s %s %s", method, uri, version);
printf("%s %s %s\n", method, uri, version);
if(strcmp(method, "GET") == 0) rmtd = M_GET;
else if(strcmp(method, "POST") == 0) rmtd = M_POST;
else if(strcmp(method, "HEAD") == 0) rmtd = M_HEAD;
else rmtd = M_NONE;
if (rmtd == M_NONE) {
clienterror(fd, method, "501", "Not Implemented",
"Tiny does not implement this method", rmtd);
return;
}
contentLen = read_requesthdrs(&rio, post_content, rmtd);
/* Parse URI from GET request */
is_static = parse_uri(uri, filename, cgiargs);
if (stat(filename, &sbuf) < 0) {
clienterror(fd, filename, "404", "Not found",
"Tiny couldn't find this file", rmtd);
return;
}
if (is_static) {/* Serve static content */
if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) {
clienterror(fd, filename, "403", "Forbidden",
"Tiny couldn't read the file", rmtd);
return;
}
serve_static(fd, filename, sbuf.st_size, rmtd);
}
else {/* Serve dynamic content */
if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) {
clienterror(fd, filename, "403", "Forbidden",
"Tiny couldn't run the CGI program", rmtd);
return;
}
if(rmtd == M_POST) strcpy(cgiargs, post_content);
serve_dynamic(fd, filename, cgiargs, rmtd);
}
}
int read_requesthdrs(rio_t *rp, char* content, int rmtd)
{
char buf[MAXLINE];
int contentLength = 0;
char *begin;
rio_readlineb(rp, buf, MAXLINE);
while(strcmp(buf, "\r\n")) {
rio_readlineb(rp, buf, MAXLINE);
printf("%s", buf);
if(rmtd == M_POST && strstr(buf, "Content-Length: ")==buf)
contentLength = atoi(buf+strlen("Content-Length: "));
}
if(rmtd == M_POST){
contentLength = rio_readnb(rp, content, contentLength);
content[contentLength] = 0;
printf("POST_CONTENT: %s\n", content);
}
return contentLength;
}
代码托管
我的错题
2.有关Web服务器的说法,正确的是()
- A.Web服务器可以提供静态和动态内容
- B.Web服务器返回的静态内容由URL标识
- C./.../app?150&21传递的内容由app可执行文件产生,150和21是调用参数
- D.最小的URL后缀“/”,会被扩展成类似“/index.html”的默认主页
错误原因
我选了ACD,没有选B.课本上P666页写,可执行文件的URL可以在文件名后包括程序参数。“?”字符分隔文件名和参数,而且每个参数都用“&”隔开。课本没有说这两个参数是可执行文件产生的,所以我没有选。
查阅资料后,这两个参数确实是由可执行文件产生的,所以答案为ABCD。
9.如图关于struct sockaddr_in和struct sockaddr,下面说法正确的是()
- A .sa_family可以是AF_INET,AF_INET6和AF_UNSPEC
- B .sa_family如果指定AF_INET,那么函数就不能返回任何IPV6相关的地址信息;如果仅指定了AF_INET6却可以返回IPV4地址信息。
- C .套接字接口中的地址类型是sturct sockaddr
- D .套接字接口中的地址类型是sturct sockaddr_in
我选的是ABC,答案是AC。
对于A选项,AF_INET(又称 PF_INET)是 IPv4 网络协议的套接字类型,AF_INET6 则是 IPv6 的;而 AF_UNIX 则是 Unix 系统本地通信。所以A是对的。
对于B选项,sin_family参数指定调用者期待返回的套接口地址结构的类型。它的值包括三种:AF_INET,AF_INET6和AF_UNSPEC。如果指定AF_INET,那么函数就不能返回任何IPV6相关的地址信息;如果仅指定了AF_INET6,则就不能返回任何IPV4地址信息。AF_UNSPEC则意味着函数返回的是适用于指定主机名和服务名且适合任何协议族的地址。
故B错误。
对于C ,课本P653页有,所以C选项是正确的。
10.有关Socket端口和Linux命令,下面说法正确的是()
- A .可以用 echo /etc/services查看
- B .使用 netstat -pan|grep 80可以查看哪些进程占用了80端口
- C .可以使用lsof -i:80查看哪里些进程占用了80端口
- D .可以用sudo /etc/init.d/service start|stop|restart 启动|停止|重启系统服务
- E .可以用netstat -pa 查看所有的服务端口(LISTEN,ESTABLISHED)
- F .ps -aux | grep pid 可以查看绑定某端口的进程号为pid的进程的详细情况
我选的是BCEF,正确答案:ABCDEF
我在终端输入这些命令,eco /etc/services没有显示端口情况。
lsof -i:80也没有任何输出。所以选错了。
所以我很疑惑,希望老师能够解答。
11.知名端口号(well-known port numbers)就是那些由互联网名称与数字地址分配机构(ICANN)预留给传输控制协议(TCP)和用户数据包协议(UDP)使用的端口号。下面有关知名端口号的说法正确的是()
- A .echo 服务的端口号是7
- B .ftp 文件传输协议(FTP)端口号是23
- C .请求主机发送日期和时间的daytime服务的端口号是13
- D .web服务器的端口号是80
- E .telnet服务的端口号是21
- F .SMTP服务的端口号是25
- G .tftp服务的端口号是69
- H .HTTPS服务的端口号是443
- I .rsync服务的端口号是873
- J .pop3服务的端口号是110
- K .auth服务的端口号是113
正确答案:ACDFGHIJK
B选项,FTP的端口号是20和21
E选项,telnet的端口号是23
18( 多选题 | 1 分)
有关域名和IP地址的说法,正确的是()
A .
1988年前,域名和IP地址的映射通过HOST.txt来完成
B .
1988年后,域名和IP地址的映射通过DNS来完成
C .
Linux中可以使用hostname(1)来展示和某个IP对应的域名
D .
域名和IP地址的映射必须是1对1 的
我选的是ABC
对于C选项,hostname只能显示或者设置主机名,不能展示某个IP对应的域名。
对于D选项,在最简单的情况下,域名和IP是一一对应的。但是,一个域名可以对应多个IP,一个ip也可以对应多个域名。
所以正确答案为AB。
结对及互评
点评模板:
-
20155324博客中值得学习的或问题:
他学习的是第八章,异常处理
-
其他
本周结对学习情况
- [20155324](http://www.cnblogs.com/wang5324/p/8053264.html)
- 结对照片
- 结对学习内容
- 理解信号机制
- 掌握管道和I/O重定向
其他(感悟、思考等,可选)
xxx
xxx
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 |
| 第十四周 | 300/5000 | 2/25 | 15/300 | |
尝试一下记录「计划学习时间」和「实际学习时间」,到期末看看能不能改进自己的计划能力。这个工作学习中很重要,也很有用。
耗时估计的公式
:Y=X+X/N ,Y=X-X/N,训练次数多了,X、Y就接近了。
-
计划学习时间:XX小时
-
实际学习时间:XX小时
-
改进情况:
(有空多看看现代软件工程 课件
软件工程师能力自我评价表)