标签:
我们需要理解基本的客户端-服务端编程模型,以及如何编写使用因特网提供的服务的客户端-服务端程序。
最后,我们将把所有这些概念结合起来,开发一个小的但功能齐全的Web服务器,能够为真实的Web浏览器提供静态的和动态的文本和图形内容。
每个网络应用程序都是基于客户端 - 服务器模型的
采用这种模型,一个应用是由一个服务器进程
和一个或多个客户端进程组成。
服务器管理某种资源,并且通过操作这种资源为它的客户端提供某种服务。
WEB服务器,代表客户端检索,执行磁盘内容。FTP服务器,为客户端进行存储和检索。电子邮件服务器,为客户端进行读和更新。客户端-服务器模型中的基本操作是事务(transaction).
客户端-服务器事务由四步组成 事务。响应,并等待下一个请求。响应并处理它。客户端和服务端通常运行在不同的主机上,并且通过计算机网络的硬件和软件资源来通信。
网络只是又一种I/O设备,作为数据源和数据接收方。
对于物理上而言,网络是一个按照地理远近组成的层次系统。
最低层是LAN(Local Area Network,局域网):在一个建筑或校园范围内。
迄今为止,最流行的LAN技术是以太网(Ethernet).
由Xerox PARC公司在20世纪70年代中期提出。
以太网被证明是适应力极强的,从3 MB/s到10 GB/s。一个以太网段(Ethernet segment)

包括一些电缆(通常是双绞线)和一个叫做集线器的小盒子。
电缆都有相同的最大位带宽 100MB/s或者1GB/S.适配器,一端连接到集线器的一个端口。集线器不加分辨地将从一个端口收到的每个位复制到其他所有端口上。 以太网段通常跨越一些小的区域。
扩展介绍以太网
每个以太网适配器(网卡)都有一个全球唯一的48位地址,它存储在这个适配器的ROM上(MAC)。
一台主机可以发送一段位,称为帧(frame),到这个网段内其他任何主机。
每个帧包括
头部(header)位 帧的源,和目的地址以及此帧的长度。有效载荷。每个主机适配器都能看到这个帧,但是只有目的主机实际读取它。
使用一些电缆和叫做网桥(bridge)的小盒子,多个以太网段可以连接称较大的局域网,称为桥接以太网(bridged Ethernet)。

电缆连接网桥与网桥,或者 网桥与集线器。 
在层次的更高级别,多个不兼容的局域网可以通过叫做路由器(router)的特殊计算机连接起来,组成一个internet(互联网络)
Internet和internet
我们总是用小写字母的internet表示一般概念,大写的Internet表示一种具体实现,如全球IP因特网。
WAN(Wide-Area Network,广域网)
互联网至关重要的特性是:
Q:如何能够让某台源主机跨过所有这些不兼容的网络发送数据位到另一台目的主机呢?
A:解决办法是一层运行在每台主机和路由器上的协议软件,消除不同网络之间的差异。
协议:控制主机和路由器如何协调工作来实现数据传输。 命名机制 互联网地址(internet address),这个地址唯一标识了这台主机。传送机制 协议通过定义一种把数据位捆扎成不连续的片(称为包)的方式。 包是由包头和有效载荷组成的。 包头 包的大小源主机和目的主机地址有效载荷包括从源主机发出的数据位
一个客户端运行在主机A上,主机A与LAN1相连,它发送了一串数据字节到运行在主机B上的服务器端,主机B则连接在LAN2上。有如下8个步骤。
A上的客户端进行系统调用,从客户端的虚拟地址空间拷贝到内核缓冲区。主机A上的协议软件通过在数据前附加互联网络包头和LAN1帧头,创建了一个LAN1的帧。
互联网包头寻址到互联网主机B。(最终目的)LAN1帧头寻址到路由器。(中转站)LAN1帧的有效载荷是互联网络包。互联网络包的有效载荷是实际的用户数据。封装是基本的网络互联方法之一。LAN1适配器拷贝该帧到网络上。
帧到达路由器,路由器的LAN1适配器从电缆上读取它,并传送到协议软件中。路由器从互联网包头中提取处目的互联网络地址,用它作为路由器的索引,确定向哪里转发这个包。
LAN1的帧头,加上寻址到主机B的新的LAN2帧头,并把得到的帧传送到适配器。路由器的LAN2适配器拷贝该帧到网络
帧到达主机B时,它的适配器从电缆上读到此帧,并将它传送到协议软件。系统调用。当然,在这里,我们掩盖了许多非常艰难的问题。
帧大小的最大值,该怎么办。帧。虽然如此,我们也能大概了解到互联网络思想的精髓。

每台因特网主机都运行实现TCP/IP协议 (Transmission Control Protocol/Intelnet Protocol,传输控制协议/互联网络协议)的软件,几乎所有计算机系统都支持这个协议。
套接字接口函数和Unix I/O函数来进行通信。 套接字函数典型地是作为会陷入内核的系统调用来实现的,并调用各种内核模式的TCP/IP函数。TCP/IP协议实际上一个协议族,每一个协议提供不同的功能。
例
IP协议提供基本的命名方法,和传递机制。
传递机制能够从一台因特网主机往其他主机发送包,也叫做数据报(datagram)IP机制从某种意义上是不可靠的,如果数据报在网络丢失或重复,并不会试图恢复。 UDP(Unreliable Datagram Protocol,不可靠数据报协议)稍微扩展了IP协议。 包可以在进程间,而不是主机间传送。TCP是一个构建在IP之上的复杂协议,提供了进程间可靠地全双工(双向)的连接。
为了简化讨论
TCP/IP看作是一个单独的整体协议。TCP和IP为应用程序提供的基本功能。UDP从程序员的角度,我们可以把因特网看作世界范围内主机的集合,满足一下特性。
32位的IP地址。IP地址可以被映射为一组称为因特网域名(Internet domain name)的标示符。连接和任何其他主机上的进程通信。一个IP地址就是一个32位无符号整数。网络程序将IP地址存放在一个IP地址结构中。
/* Internet address structure */
struct in_addr{
unsigned int s_addr;
}
为什么要用结构来存放标量
IP地址
是早期的不幸产物,但是现在更改太迟了。
因为因特网主机可以有不同的主机字节顺序
TCP/IP为任意整数数据项定义了统一的网络字节顺序(network byte order)(大端,x86是小端)。
Unix提供下面这样的函数实现转换。

IP地址通常是以一种称为点分十进制表示法来表示的
字节(8位)都是由它的十进制表示(0~255),并且用句点和其他字节间分开。在Linux系统上,你能够使用hostname命令来确定你自己主机的点分十进制:
linux> hostname -i
10.174.204.145
可以使用inet_aton和inet_ntoa函数来实现两者之间互相转换。

方便人们记忆的对于IP的映射就是域名。
域名集合形成了一个层次结构,每个域名编码了它在层次中的位置。

路径就是域名。层次结构第二层 : 一级域名(first-level domain name)
ICANN(Internet Corporation for Assigned Names and Numbers,因特尔分配名字数字协会)定义。com,edu,gov,org和net。层次结构第三层: 二级域名(second-level)
cmu.edu。ICANN的各个授权代理按照先到先服务的基础分配的。因特网定义了域名集合和IP地址直接的映射。
HOSTS.TXT 1988年,这个映射都是通过一个叫做HOSTS.TXT的文本文件来手工维护的。DNS: DNS,Domain Name System,域名系统),来维护的。DNS数据库由上百万的主机条目结构(host entry structure)组成的。 域名(一个官方名字和一个别名)和一组IP地址之间的映射。 
因特网应用程序通过调用 gethostbyname和gethostbyaddr函数,从DNS数据库中检索任意的主机条目。。

每台主机都有本地定义的域名localhost
本地送回地址(loopback address) :127.0.0.1。localhost名字为引用运行在同一机器上的客户端和服务端提供了一种便利和可移植的方式。
Internet服务端和客户端通过在连接上发送和接收字节流来通信。
点对点的。全双工的。可靠的一个套接字是连接的一个端点。
每个套接字都有相应的套接字地址。
IP地址和一个16位的整数端口组成的,用地址:端口来表示。当客户端发起一个连接请求时,客户端套接字地址中的端口由内核自动分配的。
临时端口然后,服务器套接字地址中的端口通常是某个知名的端口,和这个服务相对应的。
例如:
8025在Unix机器上,文件/etc/services 包含一张这台机器提供的服务和他们的知名端口号的综合列表。
一个连接是由它两端的套接字地址唯一确定的。
套接字对(socket pair),由下列元组来表示: (cliaddr:cliport,servaddr:servport)
套接字接口(socket interface)是一组函数,他们和Unix I/O函数结合起来,用以创建网络应用。
给出一个典型的客户端-服务器事务的上下文中套接字接口概述,以此导向。


不同的角度:
Unix内核角度来看,一个套接字就是通信的一个端点。Unix程序来看,套接字就是一个有相应描述符的打开文件。
Internet的套接字地址(Internet-sytle)存放在上图所示的类型为sockaddr_in的16字节结构中。
sin_family成员是AF_INET,ipv4还是ipv6。sin_port成员是一个16位的端口号。sin_addr成员就是一个32位的IP地址。
IP地址和端口号总是以网络字节顺序(大端法)存放的。sin_zero 是填充,使得sockaddr_in和sockaddr一样大。
sockaddr_in给程序员操作的,sockaddr交由套接字函数使用的,两者可以直接强制转换。

客户端和服务端使用socket函数来创建一个套接字描述符(socket descriptor)
跟open差不多
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain, int type ,int protocol);
返回:若成功则为非负描述符,出错为-1
我们总是带这样的参数调用socket函数:
clientfd = Socket(AF_INET,SOCK_STREAM,0);
AF_INET表面我们在使用IPV4协议。SOCK_STREAM表示这个套接字是Internet连接的一个端点。socket返回的clientfd描述符,仅仅是部分打开,还不能用于读写。 客户端通过调用connect函数来建立和服务器的连接
#include<sys/socket.h>
int connect(int sockfd,struct sockaddr *serv_addr,int addrlen);
返回:若成功则为0,若出错则为-1
connect函数试图于套接字地址为serv_addr的服务器建立一个因特网连接.
addrlen是sizeof(sockaddr_in).connect函数会阻塞,一直到连接成功建立或是发生错误。sockfd描述符就可以读写了。 (x:y,serv_addr.sin_addr,serv_addr.sin_port)刻画的。 x是客户端IP地址,而y表示临时端口。open_cilentfd是socket和connect的包装函数(不是系统自带)
#include <csapp.h>
int open_clientfd(char *hostname, int port);
返回:若成功则为描述符,若`Unix`出错则为-1,DNS出错则为-2.
open_clientfd函数和运行在hostname上的服务器建立一个连接,并在知名端口port上监听连接请求。
Unix I/O函数做输入和输出。
剩下的套接字函数bind,listen和accept被服务器用来和客户端建立链接。
#include<sys/socket.h>
int bind(int sockfd,struct sockaddr *my_addr,int addrlen);
//返回: 若成功则为0,若出错则为-1
bind函数告诉内核将my_addr中的服务器套接字地址和套接字描述符sockfd联系起来。
addrlen就是sizeof(sockaddr_in)?
客户端是发起连接请求的主动实体。服务器是等待来自客户端连接请求的被动实体。
默认情况下,内核会认为socket函数创建的描述符对应于主动套接字(active socket).
服务器调用listen告诉内核,描述符是被服务器而不是客户端使用的
#include<sys/socket.h>
int listen(int sockfd,int backlog);
返回:若成功则为0,若出错则为-1
listen函数将sockfd从一个主动套接字转化为一个监听套接字(listenning socket)。
backlog参数暗示了内核在开始拒绝连接请求之前,应该放入队列中等待的未完成连接请求的数量。 backlog参数的确切含义要求对TCP/IP协议的理解,这超出了我们的讨论的范围。1024。用socket,bind和listen函数结合称open_listenfd的包装函数。
服务器可以用它来创建一个监听描述符。
#include<csapp.h>
int open_listenfd(int port)
返回:若成功则为描述符,若Unix出错则为-1
open_listenfd函数打开和返回一个监听描述符
port上接收请求。
listenfd套接字描述符。使用setsockopt函数来配置服务器,使得它能被立即中止和重启。
接下来,初始化服务器的套接字地址结构。
INADDR_ANY来告诉内核这个服务器将接收任何IP地址到端口port的请求。 INADDR_ANY通配符地址就是指定地址为0.0.0.0的地址blind,listen。将其转换为监听套接字。CSAPP这里介绍的十分有问题,所以特地翻了UNIX 网络编程的原话。

用于从已完成连接队列队头返回下一个已完成连接。
返回三个值
已连接标示符客户端地址客户度地址长度监听描述符和已连接描述符之间的区别是很多人迷惑。
监听描述符是作为客户端连接请求的一个端点。
已连接描述符是客户端和服务器之间已经建立起来的连接的一个端点。
学习套接字接口的最好办法是研究示例代码。
没办法。这个代码估计也有挺多疑惑,还是过段时间啃Unix网络编程把
这块写过servlet,就不用复述了,以后再详细补
标签:
原文地址:http://blog.csdn.net/zy691357966/article/details/51526969