所有的网络应用都是基于相同的基本编程模型,有着类似的整体逻辑结构,并且依赖相同的编程接口。
网络应用依赖很多系统研究中的概念:进程、信号、字节顺序、存储器映射以及动态存储分配。还有一些诸如客户端-服务器编程模型的新概念。
客户端-服务器编程模型
每个网络应用都是基于客户端-服务器模型的。在该模型中,一个应用是由一个服务器进程和一个或多个客户端进程组成的。
服务器管理某种资源。一个Web服务器管理了一组磁盘文件,它会代表客户端进行检索和执行;一个FTP服务器管理了一组磁盘文件,它会为客户端进行存储和检索;一个Emial服务器管理了一些文件,它为客户端进行读和更新。
客户端-服务器模型中的基本操作是事务,它由四步组成:
- 客户端向服务器发送一个请求,发起一个事务;
- 服务器收到请求后,解释之,并操作它的资源;
服务器给客户端发送一个响应,例如将请求的文件发送回客户端;
客户端收到响应并处理它,例如Web浏览器在屏幕上显示网页。
认识到客户端和服务器是进程而不是具体的机器或主机是重要的。
网络
对于一个主机而言,网络只是又一种I/O设备,作为数据源和数据接收方。
最流行的局域网是以太网(Ethernet),一个以太网段包括一些电缆(通常是双绞线)和一个集线器。每根电缆都有相同的带宽,它们一端连接到主机的适配器,另一端则连接到集线器的一个端口上。集线器不加分辨地从一个端口上收到的每个位复制到其他所有的端口上。
每个以太网适配器都有一个全球唯一的48位地址,一台主机可以发送一帧数据到这个网段内的其他主机。每个帧包括了固定数量的头部位,用来标识此帧的源和目的地址,以及此帧的长度,之后便是数据位的有效载荷。每个网络适配器都能看到这个帧,但是只有目的主机才能实际读取它。
通过网桥,多个以太网段可以连接成较大的局域网,称为桥接以太网。网桥比集线器更充分地利用了网线带宽。
在更高的层次中,多个不兼容的局域网可以通过路由器连接,组成一个互联网。
互联网的一个重要特性是,它能连接完全不兼容的局域网和广域网,方法是通过协议软件,它消除了不同网络之间的差异。这种协议必须提供两种基本能力:
- 命名机制,每台主机被分配至少一个互联网络地址,这个地址唯一地标识了这台主机。
- 传送机制。定义包含包头和有效载荷的数据包。
全球IP因特网
从程序员的角度,可以把因特网看做一个世界范围的主机集合,它满足以下特性:
- 主机集合被映射成一组32位的IP地址。
- 这组IP地址被映射成一组叫做因特网域名的标识符。
- 因特网主机上的进程能够通过连接和任何其他因特网主机上的进程通信。
IP地址
一个IP地址就是一个32位无符号整数,它存放在一个IP地址结构中。
TCP/IP协议为整数数据项定义了统一的网络字节顺序(大端字节顺序)。IP地址也被以大端法存放。
IP地址通常是以一种称为点分十进制表示法来表示的。例如128.2..194.242就是地址0x8002c2f2的点分十进制表示。
因特网域名
客户端和服务器通信时使用的是IP地址,然而对于人们而言大整数是很难记住的,因此因特网也定义了一组更加人性化的域名,以及一种将域名映射到IP地址的机制。
域名是一串用点分隔的单词(字母、数字和破折号),它有自己的层级结构。
除了根节点,第二层是一组一级域名,常见的一级域名有com、edu、gov、org等。
二级域名如mit、berkeley、csdn等。
域名集合和IP地址集合之间的映射由分布世界范围内的数据库(DNS,域名系统)来维护。
一个域名可以与一个IP地址一一对应;或者多个域名映射到多个IP地址;或者某些合法的域名没有IP地址的映射。
因特网连接
客户端和服务器的连接是点对点、全双工、可靠的。
一个套接字是连接的一个端点。每个套接字都有对应的套接字地址,它由一个IP地址和一个16位的整数端口组成,用“地址:端口”表示。
当客户端发起一个连接请求时,客户端套接字地址中的端口是由内核自动分配的,称为临时端口。然而,服务器套接字地址中的端口通常是某个知名的端口,是和这个服务相对应的。例如,Web服务器通常使用端口80,Email服务器通常使用端口25。
一个连接是由它两端的套接字地址唯一确定的,这对套接字地址叫做套接字对(socket pair)。由下列元祖来表示:
(cliaddr:cliport,servaddr:servport)
- 1
套接字接口
套接字接口是一组函数,它们和Unix I/O函数结合起来,用以创建网络应用。
socket 函数
客户端和服务器使用socket函数来创建一个套接字描述符。
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain,int tpye,int protocol);
clientfd=socket(AF_INET,SOCK_STREAM,0);
- 1
- 2
- 3
- 4
- 5
- 6
其中AF_INET表示我们正在使用因特网,而SOCK_STREAM表示这个套接字是因特网连接的一个端点。
connect函数
客户端通过调用connect函数来建立与服务器的连接。
#include <sys/socket.h>
int connect(int sockfd,struct sockadd *serv_addr,int addrlen);
- 1
- 2
- 3
connect函数试图与套接字地址位serv_addr的服务器建立连接,它被阻塞直到连接成功或发生错误。
bind函数
下面的bind、listen和accept函数被服务器用来与客户端建立连接。
#include <sys/socket.h>
int bind(int sockfd,struct sockaddr *my_addr,int addrlen);
- 1
- 2
- 3
bind函数告诉内核将套接字地址和套接字描述符联系起来。
listen函数
服务器是被动接收客户端连接请求的,listen函数将套接字描述符从主动套接字转化为监听套接字。
accept函数
服务器通过调用accept函数来等待来自客户端的连接请求。
#include <sys/socket.h>
int accept(int listenfd,struct sockaddr *addr,int *addrlen);
- 1
- 2
- 3
accpet函数等待来自客户端的连接请求到达监听描述符listenfd,然后在addr中填写客户端的套接字地址,并返回一个连接描述符connfd。
监听描述符和连接描述符的区别
监听描述符是供客户端连接请求的使用一个端点,它被创建一次,并存在于服务器的整个生命周期。
连接描述符是客户端和服务器之间已成功连接的一个端点,服务器每次接受连接请求时都会创建一次,它只存在于服务器每次为一个客户端服务的过程中。
区分监听描述符和连接描述符是有必要的,因为这样使得我们可以建立并发服务器,它能够同时处理许多客户端连接。
Web服务器
Web基础
Web服务用的是基于文本的应用级协议:HTTP(超文本传输协议)。一个Web客户端(即浏览器)打开一个到服务器的连接,请求某些内容。服务器响应所请求的内容,然后关闭连接。浏览器读取这些内容,并把它显示在屏幕上。
Web服务和常规的文件检索服务(如FTP)的主要区别是Web内容可以用HTML(超文本标记语言)编写。HTML可以定义网页显示的内容,以及创建超链接。
Web内容
Web内容是与MIME(多用途网际邮件扩充协议)类型相关的字节序列,包括:HTML页面、无格式文本、Postscript文档、GIF图像、JPEG图像等。
Web服务器以两种不同的方式向客户端提供内容:
- 静态内容,取一个磁盘文件,并将它的内容返回给客户端。
- 动态内容,运行一个可执行文件,并将它的输出返回给客户端。
每种内容都和某个文件相关联,每个文件都用URL(统一资源定位符)唯一标识。例如
http://www.google.com:80/index.html
- 1
可执行文件的URL可以在文件名后包含程序参数,“?”分隔文件名和参数,多参数用“&”分隔开。如
http://bluefish.ics.cs.cmu.edu:8000/cgi-bin/adder?15000&213
- 1
标识了一个叫做cgi-bin/adder
的可执行文件,带有两个参数字符串15000和213。
当用户键入一个URL时,客户端和服务器使用的是URL的不同部分。客户端使用前缀
http://bluefish.ics.cs.cmu.edu:8000
- 1
来决定与在哪里的哪类服务器连接。
而服务器使用后缀
/cgi-bin/adder?15000&213
- 1
来发现文件系统中的文件。
HTTP事务
HTTP是基于在因特网连接上传送的文本行的,可以使用Unix的TELNET程序来和Web服务器通信。
HTTP请求
一个HTTP请求的组成是这样的:一个请求行
GET / HTTP/1.1
- 1
后面跟随0个或多个请求报头
Host: www.aol.com
- 1
再跟随一个空的文本行来终止报头列表。
- 1
一个请求行的格式是
<method> <uri> <version>
- 1
HTTP支持许多不同的方法,包括GET/POST/OPTIONS/HEAD/PUT/DELETE/TRACE。其中GET最常用。
GET方法指导服务器生成和返回URI(统一资源标识符,是URL的后缀,包括文件名和可选的参数)。
请求报头为服务器提供了额外的信息,例如浏览器的商标名等。
HTTP响应
HTTP响应和请求是类似的,它包括:一个响应行,后面跟随0个或多个响应报头,然后是终止报头的空行,再跟随一个响应主体。
一个响应行的格式为:
<version> <status code> <status message>
- 1
版本字段描述的是响应所遵循的HTTP版本。状态码则是一个三位的正整数,指明对请求的处理。
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/markdown_views-ea0013b516.css">
</div>