信息安全系统设计基础第十二周总结

第 11 章 网络编程

11.1 客户端-服务器编程模型

每个网络应用都是基于客户端一服务器模型的。一个应用是由一个服务器进程和一个或者多个客户端进程组成。服务器管理某种资源,并且通过操作这种资源来为它的客户端提供某种服务。
客户端一服务器模型中的基本操作是事务,由四步组成:

1.当一个客户端需要服务时,它向服务器发送一个请求,发起一个事务。

2.服务器收到请求后,解释它,并以适当的方式操作它的资源。

3.服务器给客户端发送一个响应,并等待下一个请求。

4.客户端收到响应并处理它。

11.2 网络

客户端和服务器通常运行在不同的主机上,并且通过计算机网络的硬件和软件资源来通信。
对于一个主机而言,网络只是又一种I/O设备,作为数据源和数据接收方。

一个网络主机的硬件组成:

物理上而言,网络是一个按照地理远近组成的层次系统。最低层是 LAN (Local Area Network,局域网),最流行的局域网技术是以太网 (Ethernet)。
一个以太网段 (Ethernet segment) 包括一些电缆(通常是双绞线)和一个叫做集线器的小盒子。

每个以太网适配器都有一个全球唯一的 48 位地址,它存储在这个适配器的非易失性存储器上。一台主机可以发送 一段位,称为帧(frame),到这个网段内其他任何主机。每个帧包括一些固定数量的头部 (header) 位,用来标识此帧的源和目的地址以及此帧的长度,此后紧随的就是数据位的有效载荷。每个主机适配器都能看到这个帧,但是只有目的主机实际读取它。

网桥比集线器更充分地利用了电缆带宽。

多个不兼容的局域网可以通过叫做路由器 (router)的特殊计算机连接起来,组成一个internet(互联网络)。

路由器可以用来由各种局域网和广域网构建互联网络。

互联网络至关重要的特性是,它能由采用完全不同和不兼容技术的各种局域网和广域网组成。

运行在每台主机和路由器上的协议软件,它消除了不同网络之间的差异。提供两种基本能力 :

1.命名机制。不同的局域网技术有不同和不兼容的方式来为主机分配地址。互联网络协议通过定义一种一致的主机地址格式消除了这些差异。每台主机会被分配至少二个这种互联网络地址 (internet address),这个地址唯一地标识了这台主机。

2.传送机制。在电缆上编码位和将这些位封装成帧方面,不同的联网技术有不同的和不兼容的方式。互联网络协议通过定义一种把数据位捆扎成不连续的片(称为包)的统一方式,从而消除了这些差异。一个包是由包头和有效载荷组成的,其中包头包括包的大小以及源主机和目的主机的地址,有效载荷包括从源主机发出的数据位。

在互联网络上,数据是如何从一台主机传送到另一台主机的:

11.3 全球 IP 因特网

每台因特网主机都运行实现 TCP/IP 协议 (Transmission Control Protocol/Internet Protocol,传输控制协议/互联网络协议)的软件,几乎每个现代计算机系统都支持这个协议。

因特网的客户端和服务器混合使用套接字接口函数和 Unix I/O 函数来进行通信。套接字函数典型地是作为会陷入内核的系统调用来实现 的,并调用各种内核模式的 TCP/IP 函数。

TCP/IP 实际上是一个协议族,其中每一个都提供不同的功能。

1.IP 协议提供基本的命名方法和递送机制,这种递送机制能够从一台因特网主机往其他主机发送包,也叫做数据报 (datagram). IP 机制从某种意义上而言是不可靠的,因为,如果数据报在网络中丢失或者重复, 它并不会试图恢复。

2.UDP(Unreliable Datagram Protocol,不可靠数据报协议)稍微扩展了 IP 协议, 这样一来,包可以在进程间而不是在主机间传送。

3.TCP 是一个构建在 IP 之上的复杂协议,提供 了进程间可靠的全双工(双向的)连接。

把因特网看做一个世界范围的主机集合,满足以下特性:

1.主机集合被映射为一组 32 位的 IP 地址。

2.这组IP地址被映射为一组称为因特网域名 (lnternet domain name) 的标识符。

3.因特网主机上的进程能够通过连接(connection )和任何其他因特网主机上的进程通信。

11.3.1 IP地址

一个 IP 地址就是一个 32 位无符号整数。

因为因特网主机可以有不同的主机字节顺序, TCP/IP 为任意整数数据项定义了统一的网络字节顺序 (network byte order) (大端字节顺序)。

IP 地址,它放在包头中跨过网络被携带。

IP 地址结构中存放的地址总是以(大端法)网络宇节顺序存放的,即使主机字节顺序 (host byte order) 是小端法。

htonl 函数将 32 位整数由主机字节顺序转换为网络字节顺序。 ntohl 函数将 32 位整数从网络宇节顺序转换为主机字节。 htons和 ntohs 函数为 16 位的整数执行相应的转换。

IP 地址通常是以一种称为点分十进制表示法来表示的。

因特网程序使用 inet_aton 和 inet_ntoa 函数来实现 IP 地址和点分十进制串之间的转换:

11.3.2 因特网域名

因特网客户端和服务器互相通信时使用的是 IP 地址。

子树称为子域 (subdomain)。 层次结构中的第一层是 一个未命名的根节点。下一层是一组一级域名 (first-level domain name)。
常见的第一层域名包括 com、 edu、 gov、org 和 net。
下一层是二级 (second-level) 域名,例如 cmu. edu

一旦一个组织得到了一个二级域名,那么它就可以在这个子域中创建任何新的域名了。

因特网定义了域名集合和 IP 地址集合之间的映射。

因特网应用程序通过调用 gethostbyname 和 gethostbyaddr 函数,从 DNS 数据库中检索任意的主机条目。

gethostbyname 函数返回和域名 name 相关的主机条目。 gethostbyaddr 函数返回和 IP 地址 addr 相关联的主机条目。第二个参数给出了一个 IP 地址的字节长度,对于目前的因特网而言总是四个字节。对于我们的要求来说,第三个参数总是0。

每台因特网主机都有本地定义的域 名localhost,这个域名总是映射为本地回送地祉 (loopback address) 127.0.0.1

1.最简单的情况下,一个域名和一个 IP 地址之间是一一映射的

2.某些情况下,多个域名可以映射为同一个 IP 地址

3.最通常的情况下,多个域名可以映射到多个 IP 地址

4.某些合法的域名没有映射到任何 IP 地址

11.3.3 因特网连接

因特网客户端和服务器通过在连接上发送和接收字节流来通信。
连接是点对点的。
从数据可以同时双向流动的角度来说,它是全双工的。可靠的。

一个套接字是连接的一个端点。每个套接字都有相应的套接字地址,是由一个因特网地址和 一个 16 位的整数端口组成的,用"地址 : 端口"来表示。当客户端发起一个连接请求时,客户 端套接字地址中的端口是由内核自动分配的,称为临时端口(ephemeral port)。服务器套接字地址中的端口通常是某个知名的端口,是和这个服务相对应的。

11.4 套接字接口

套接字接口 (socket interface) 是一组函数,它们和 Unix I/O 函数结合起来,用以创建网络 应用。

套接字接口描述:

11.4.2 socket 函数

客户端和服务器使用 socket 函数来创建一个套接字描述符

11.4.3 connect 函数

客户端通过调用 connect 函数来建立和服务器的连接。

11.4.4 open_clientfd 函数

 open_clientfd 函数和运行在主机 hostname 上的服务器建立一个连接,并在知名端口 port 上监听连接请求。它返回一个打开的套接宇描述符,该描述符准备好了,可以用 Unix I/O 函数做输入和输出。

11.4.5 bind 函数

bind、 listen 和 accept 被服务器用来和客户端建立连接。

11.4.6 listen 函数

客户端是发起连接请求的主动实体。服务器是等待来自客户端的连接请求的被动实体。默认情况下,内核会认为 socket 函数创建的描述符对应于主动套接字 (active socket),它存在 于一个连接的客户端。服务器调用 listen 函数告诉内核,描述符是被服务器而不是客户端使用的。

11.4.7 open_listenfd 函数

socket、 bind 和 listen 函数结合成一个叫做。open_listenfd 的辅助函数

open_listenfd 函数打开和返回一个监听描述符,这个描述符准备好在知名端口 port 上接收连接请求。

11.4.8 accept 函数

accept 函数来等待来自客户端的连接请求

11.4.9 echo 客户端和服务器的示例

1.服务器一次一个地在客户端间迭代,称为迭代服务器 (iterative server)。

2.并发服务器 (concurrent server),它能够同时处理多个客户端。

11.5 Web 服务器

11.5.1 Web 基础

Web 客户端和服务器之间的交互用的是一个基于文本的应用级协议,叫做 HTTP (Hypertext Transfer Protocol,超文本传输协议). HTTP 是一个简单的协议。一个 Web 客户端(即浏览器) 打开一个到服务器的因特网连接,并且请求某些内容。服务器响应所请求的内容,然后关闭连接。浏览器读取这些内容,并把它显示在屏幕上。

Web 内容可以 用一种叫做 HTML (Hypertext Markup Language,超文本标记语言)的语言来编写。一个 HTML 程序(页)包含指令(标记),它们告诉浏览器如何显示这页中的各种文本和图形对象。

11.5.2 Web 内容

Web 服务器以两种不同的方式向客户端提供内容:

1.取一个磁盘文件,并将它的内容返回给客户端。磁盘文件称为静态内容 (static content), 而返回文件给客户端的过程称为服务静态内容 (serving static content)。

2.运行一个可执行文件,并将它的输出返回给客户端。运行时可执行文件产生的输出称为态内容 (dynamic content),而运行程序并返回它的输出到客户端的过程称为服务动态内容 (serving dynamic content)。

URL 的后缀:

1.确定一个 URL 指向的是静态内容还是动态内容没有标准的规则。每个服务器对它所管理的文件都有自己的规则。一种常见的方法是,确定一组目录,例如 cgi-bin,所有的可执行性文件都必须存放这些目录中。

2.后缀中的最开始的那个"/"不表示 Unix 的根目录。相反,它表示的是被请求内容类型 的主目录。例如,可以将一个服务器配置成这样: 所有的静态内容存放在目录 /usr/ httpd/html 下,而所有的动态内容都存放在目录 /usr/httpd/cgi-bin 下。

3.最小的 URL 后缀是"/"字符,所有服务器将其扩展为某个默认的主页,例如 /index. html。这解释了为什么简单地在浏览器中键人一个域名就可以取出一个网站的主页。浏览器在 URL 后添加缺失的 "/", 并将之传递给服务器,服务器又把"/"扩展到某个默认的文件名。

11.5.3 HTTP 事务

因为 HTTP 是基于在因特网连接上传送的文本行的,我们可以使用 Unix 的TELNET程序来和因特网上的任何 Web 服务器执行事务。

1.HTTP 请求

一个 HTTP 请求的组成是这样的:一个请求行 (request line) (第 5 行),后面跟随零个或更多个请求报头 (request header) (第 6 行),再跟随一个空的文本行来终止报头列表

HTTP 支持许多不同的方法,包括 GET、 POST、 OPTIONS、 HEAD、 PUT、 DELETE 和 TRACE。

GET 方法指导服务器生成和返回URI (Uniform Resource Identifier,统一资源标识符)标识的内容。 URI是相应的 URL 的后缀,包括文件名和可选的参数。

代理缓存 (proxy cache) 会使用 Host 报头,这个代理 缓存有时作为浏览器和管理被请求文件的原始服务器 (origin server) 的中介。客户端和原始服务器之间,可以有多个代理,即所谓的代理链 (proxy chain)。 Host 报头中的数据指示了原始服务器 的域名,使得代理链中的代理能够判断它是否可以在本地缓存中拥有一个被请求内容的副本。

2.HTTP 晌应

一个 HTTP 响应的组成是这样的:一个响应行 (response line) (第 8 行)后面跟随着零个或更多的响应报头 (response header) (第 9 ~ 13 行), 再跟随一个终止报头的空行(第 14 行),再跟随一个响应主体 (response body)

状态码 (status code) 是一个三位的正整数, 指明对请求的处理。状态消息 (status message) 给出与错误代码等价的英文描述。

第 12 章 并发编程

12.1基于进程的并发编程

最简单的构造并发程序的方法是进程。

下图是基于进程的并发echo服务器:

12.2基于I/O多路复用的并发编程

基本思路:使用select函数,要求内核挂起进程,只有在一个或者多个I/O事件发生后,才将控制返回给应用程序。

select函数处理类型为fd_set的合集,也叫做描述符合集。当且仅当bk=1时,,描述符k才表明是描述符集合里的一个元素。

只允许你对操作符集合做三件事:1.分配它们;2.将一个此种类型的变量赋值给另一个变量;3.用FD_ZERO,FD_SET,FD_CLR,FD_ISSET宏指令来修改和检查它们。

select函数有两个输入:一个称为读集合的描述符合集和该读集合的基数(n)。

当且仅当一个从该描述符读取一个字节的请求不会阻塞时,描述符K就准备好可以读了。

基于I/O多路复用的 并发事件驱动服务器

    将逻辑流模型化为状态机。一个状态机就是一组状态、输入事件和转移,其中一个转移就是将状态和输入事件映射到状态。每个转移都将一个(输入状态,输入事件)对映射到一个输出状态。自循环是同一输入输出状态的转移。一个状态机从某种初始状态开始执行,每个输入事件都会引发一个当前状态到下一个状态的转移。

    服务器使用I/O多路复用,借助select函数检测输入事件的发生。当每个已连接描述符准备好可读时,服务器就为相应的状态机执行转移,在这里就是从描述符读和写回一个文本行。

12.3基于线程的并发编程

线程就是运行在进程上下文中的逻辑流。

posix线程

 

12.4多线程程序中的共享变量

线程存储器模型

一组并发线程运行在一个进程的上下文中。每个线程都有它自己独立的线程上下文,包括线程ID、栈、栈指针、程序计数器和通用目的寄存器值。每个线程和其他线程一起共享进程上下文的其他部分。这包括整个用户虚拟地址空间,它是由只读文本(代码)、读写数据、堆以及所有共享库代码和数据区域组成的。线程也共享同样的打开文件的集合。寄存器从不共享。

将变量映射到存储器

线程化的C程序中变量根据它们的存储类型被映射到虚拟存储器:

1.全局变量

2.本地自动变量

3.本地静态变量

共享变量

一个变量v是共享的,当且仅当它的一个实例被一个以上的线程引用。

12.5用信号量同步线程

共享变量引入了同步错误。

进度图

进度图将n个并发线程的执行模型化为一条n维笛卡尔空间中的轨迹线。每条轴K对应线程K的进度。每个点(I1 I2~IN)代表线程K(k=1,~,n)已经完成了指令Ik这一状态。图的原点对应于没有任何线程完成这一指令的初始状态。

信号量

解决同步不同执行线程问题。

信号量s是具有非负整数值的全局变量,有两种特殊的操作来处理(P和V):

1.P(s):如果s非零,P将s减1,并且立即返回。如果s为零,那么就挂起这个线程,直到s变为非零。

2.V(s):V操作将s加1。

当有多个线程在等待同一个信号量时,你不要能预测V操作重启哪一个线程。

使用信号量来实现互斥

基本思想:将每个共享变量(或者一组相关的共享变量)与另一个信号量S(初始为1)联系起来,然后用P(S)和V(S)操作将相应的临界区包围起来。

生产—消费者问题:

12.6使用线程提高并行性

最直接的方法是将序列划分成t个不相交的区域,然后给t个不同的线程每个分配一个区域。假设n是t的倍数,这样每个区域有N/T个元素。主线程创建T个对等地运行线程,每个对等线程k并行地运行在它自己的处理的核上,并计算Sk,SK是区域k中元素的和。一旦对等线程计算完毕,主线程通过把每个Sk都加起来,计算出最终的结果。

12.7其他并发问题

四个线程不安全函数类:

  • 不保护共享变量的函数类
  • 保持跨越多个调用的状态的函数
  • 返回指向静态变量的指针的函数
  • 调用线程不安全函数的函数

可重入性

一种重要的线程安全函数——可重入函数。当它们被多个线程调用的时候,不会引用任何共享数据。

1.显式可重入

2.隐式可重入

竞争

当一个程序的正确性依赖于一个线程要在另一个线程到达y点之前到达它的控制流中的x点时,就会发生竞争。

为了消除竞争,我们可以动态地为每个整数ID分配一个独立的块,并且传递给线程例程一个指向这个块的指针。

死锁

死锁:一组线程被阻塞,等待一个永远都不会为真的条件。

参考资料

1.教材:第十一、十二章,详细学习指导:http://group.cnblogs.com/topic/73069.html

2.课程资料:https://www.shiyanlou.com/courses/413 实验十,课程邀请码:W7FQKW4Y

3.教材中代码运行、思考一下,读代码的学习方法:http://www.cnblogs.com/rocedu/p/4837092.html

4.内容总结:http://www.tuicool.com/articles/qAZnea

遇到的问题

1.bind,connct,accept,send等函数的使用顺序,有关socket的都没弄太明白

2.线程那一章都有点不太明白啊

原文地址:https://www.cnblogs.com/baka/p/5023011.html