顾客服务员程序设计要点.doc_第1页
顾客服务员程序设计要点.doc_第2页
顾客服务员程序设计要点.doc_第3页
顾客服务员程序设计要点.doc_第4页
顾客服务员程序设计要点.doc_第5页
已阅读5页,还剩22页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

n更多企业学院: 中小企业管理全能版183套讲座+89700份资料总经理、高层管理49套讲座+16388份资料中层管理学院46套讲座+6020份资料国学智慧、易经46套讲座人力资源学院56套讲座+27123份资料各阶段员工培训学院77套讲座+ 324份资料员工管理企业学院67套讲座+ 8720份资料工厂生产管理学院52套讲座+ 13920份资料财务管理学院53套讲座+ 17945份资料销售经理学院56套讲座+ 14350份资料销售人员培训学院72套讲座+ 4879份资料第四章 顾客服务员程序设计4.1 网络服务模式近年来,在信息系统中广泛使用的信息共享模型是顾客/服务器模型,这种计算模式迅速取代了以主机为主导的集中式计算方法。顾客/服务器计算具有自己的一组行业术语。表4-1列出了一些术语,这些术语经常出现在对顾客/服务器产品和应用的描述中。表4-1 顾客/服务器术语术语说明应用程序编程接口(API)一组支持顾客/服务器之间进行相互通信的函数和可调用程序顾客一个服务的请求方,通常是一个末端系统,能够从服务器处查询信息中间件一组驱动程序、应用程序编程接口或其它软件,用于改善顾客/服务器之间的连接关系数据库是一种把信息访问限制于按照搜索条件选择数据的数据库服务器是一台计算机,通常是一台高性能工作站、小型机或大型机,拥有供网络中众多用户访问的信息结构化查询语言(SQL)由IBM开发、由ANSI标准化的一种语言,用于对关系数据库的寻址、创建、更新和查询顾客/服务器环境中最基本元素是顾客和服务器。顾客通常是PC或工作站,为端用户提供非常友好的界面,例如微软的Windows等。服务器为顾客提供一组共享的用户服务程序。最常见的是数据库服务器,服务器能够使很多顾客共享对同一信息源的访问。除了顾客和服务器,组成顾客/服务器模型的第三个基本要素是网络系统。顾客。服务器计算是分布式计算。用户、应用程序和资源是分布式的,用来响应实际业务请求,并且它们通过局域网、广域网或Internet连接起来。顾客/服务器模型与分布式处理有很多不同点,主要有:l 在用户自己的系统中为该用户提供界面友好的应用程序。这使得用户可以在很大程度上控制时间安排和计算机使用类型,并使得部门管理者具有响应本地需求的能力。l 尽管应用是分散的,但仍然强调数据的集中以及很多网络管理和使用功能的集中。l 对于用户组织和厂商来说,他们有一个共同的承诺,使系统开放和模块化。这意味着用户在选择产品和混合使用来自众多厂家的设备时具有很大的选择性。l 网络互联是操作的基础,网络管理和网络安全在组织和操作系统中具有很高的优先权。4.1.1 顾客/服务器应用顾客/服务器体系结构的核心是应用程序级任务在顾客和服务器之间的分配。图4-1给出了这个模型的一般情况。无论是顾客还是服务器,最基本的软件是运行在硬件平台上的操作系统,顾客的平台和操作系统可能和服务器的不同。事实上,在网络环境下,可能会有很多不同类型的顾客平台和操作系统以及很多类型的服务器平台和操作系统。只要特定的顾客和服务器共享相同的通信协议并支持相同的应用程序,低层的细节不必考虑。顾客服务器请求/应答表示服务应用逻辑(服务器部分)应用逻辑(顾客部分)通信软件通信软件协议交互服务器操作系统顾客操作系统硬件平台硬件平台图4-1 顾客/服务器体系结构使顾客和服务器能够交互的基础是通信软件,这种软件主要例子是TCP/IP。所有这些支持软件(通信软件和操作系统)的主要任务是,为分布式的应用程序提供一个基本结构。在理论上,应用程序所执行的实际功能可以针对顾客和服务器分割开来,方法是使平台和网络资源达到最优化。且使用户执行各种任务及相互之间合作使用共享资源的能力达到最优化。在某些情况下,这些都要求大批的应用程序软件在服务器上执行,而在其它一些情况下,多数应用程序逻辑上位于顾客端。顾客/服务器环境能够成功的一个基本因素是用户将系统当作一个整体而与之打交道的方式。因此,顾客端的用户界面的设计是十分重要的。在大多数顾客/服务器系统中,都突出强调了要提供易于使用、易于学习、功能强大并且灵活的图形用户界面(GUI)。4.1.2 顾客/服务器应用程序分类在顾客/服务器的通用框架中,对顾客和服务器的工作划分有许多不同的实现方法。图4-2针对数据库应用说明了可以以多种方式来分配处理过程,图中概括了数据库应用的一些主要选项。当然也存在其它的划分方法,并且对于其它不同类型的应用选项也可能具有不同的特点。服务器顾客表示逻辑应用逻辑数据库逻辑DBMS(a) 基于主机的处理服务器顾客表示逻辑应用逻辑数据库逻辑DBMS(b) 基于服务器的处理服务器顾客表示逻辑应用逻辑应用逻辑数据库逻辑DBMS(c) 合作处理服务器顾客表示逻辑应用逻辑数据库逻辑数据库逻辑DBMS(d) 基于顾客的处理图4-2 顾客/服务器应用程序分类图4-2描述了4种网络服务类型,它们分别是:l 基于主机的处理:基于主机的处理并不是真正的用户普遍认同的顾客/服务器计算。而且,基于主机的处理是指传统的大型机环境,实质上所有的处理都是在一台中心主机上完成的。用户接口通常是通过一台哑终端,即使用户在使用一台微机,用户终端也是局限于终端仿真器的角色。l 基于服务器的处理:顾客/服务器配置的最基本的一类,即顾客端主要负责提供图形化用户界面,而实质上所有的处理都是在服务器上完成的。这种配置是早期的顾客/服务器模式,尤其是部门级系统的典型。在这种配置背后的基本原理是用户工作站最适合提供良好的用户界面,并且数据库和应用程序很容易在中心系统上维护。l 基于顾客的处理:在另一个极端,实际上所有应用处理可以全部在顾客端完成。一个例外是最适合在服务器上执行的数据确认例程和其它数据库逻辑功能。一般地,某些更复杂的数据库逻辑功能都位于顾客端。这种结构可能是当今使用最普遍的顾客/服务器方式,使用户能够使用适合本地需要的应用。l 合作处理:在合作处理配置方式中,应用处理是以最优化的方式来执行的,充分利用了顾客和服务器两方面的优势以及数据的分布性。这样一种配置在设置和维护方面更加复杂,但从长远看,这种配置类型比其它类型可以为用户更好的服务质量和更高的网络效率。图4-2c和图4-2d对应的配置情况是在顾客端上有相当大的一部分负载。这种所谓的“胖顾客”(fat client)模型已经被像Powersoft公司的PowerBuilder和Gupta公司的SQL Windows这样的应用程序开发工具所采用。使用这些工具开发的应用在范围上是部门级的,支持25到150个用户。胖顾客的主要优点是它充分利用了桌面功能,卸除了服务器上的应用处理并使它们更加有效,不易发生瓶颈。然而,胖顾客策略也存在许多缺点,随着更多功能累加起来,快速地超出了桌面机器的容量,迫使机器进行升级。如果模型扩充,超出了部门的界限,合并了很多用户,则必须安装高性能局域网来支持瘦服务器和胖顾客之间大量的数据传输。最后,维护、升级或替换分布于数十台或百台桌面机的应用程序是非常困难的。图4-2b代表了一种瘦顾客(thin client)的方式,这种方式更近似地模仿了传统的以主机为中心的方式,常常是使应用程序从大型机环境发展到分布式环境的移植途径,是目前流行的Internet网络环境下的应用程序开发模式。4.1.3 三层顾客/服务器结构传统的顾客/服务器结构包括两级(或称两层):顾客层和服务器层。近年来,一种三层结构变得日益流行,如图4-3所示。在这种结构中,应用软件分布在三种类型的机器上:用户机器、中间层服务器以及后端服务器。用户机器是顾客,在三层结构中,它一般是一种瘦型顾客。中间层机器基本上是位于顾客和很多后端数据库服务器之间的连接器。中间层机器能够转换协议,从一种类型的数据库系统映像为另一种。另外,中间层机器能够融合来自不同数据源的结果。最后,中间层机器也可以充当桌面应用程序和后端应用程序之间的连接器。在中间层服务器和后端服务器之间的交互也遵从顾客/服务器的模式。因此,中间层服务器同时充当着顾客和服务器。顾客中间层服务器(应用程序服务器)后端服务器后端服务器图4-3 三层顾客/服务器结构4.1.4 中间件顾客/服务器产品的开发和使用缺少标准化,使得实现集成的、多厂商的、企业范围的顾客/服务器配置变得困难,因为顾客/服务器方式的大多数优点与其模块化以及将平台和应用程序混合、协调起来提供商业解决办法的能力紧密相连的,这种互操作问题必须得到很好的解决。为了获得顾客/服务器的优点,开发者必须开发一组工具,为跨越所有平台访问系统资源提供唯一的方法和形式。这使程序员能够构件这样的应用程序:在不同的PC 机和工作站上所见所感相同,而且无论数据在什么位置都使用相同的方法来访问数据。满足这一要求的最普遍的方法是,在上层应用程序和下层通信软件和操作系统之间使用标准编程接口和协议。这种标准化的接口和协议称为中间件(middleware)。具有了标准的编程接口,在不同的服务器类型和工作站类型上实现相同的应用就很容易了。这对于用户来说具有明显的好处,而厂商也受到市场的驱动来提供这样的接口。主要原因是用户购买应用程序而不是服务器;用户将只选择那些运行了他们希望的应用程序的服务器。需要标准化的协议将这些不同的服务器接口与需要访问它们的顾客连接起来。目前已经有很多中间件软件包,从非常简单的到非常复杂的。它们所具有的共同特点是隐藏不同网络协议和操作系统的复杂性和不一致性。顾客和服务器厂商一般都提供了很多非常流行的中间件软件包供选择,这样,用户可以决定一个特定的中间件策略,然后从各个厂商那里汇集装置,来支持这个策略。图4-4给出了在顾客/服务器结构中中间件的作用。注意,中间件具有顾客端组件和服务器端组件两部分,中间件的基本目的是使位于顾客端的应用程序或用户能够访问服务器上的各种服务,同时不需考虑服务器之间的差别。对于特定的应用领域,结构化查询语言(SQL)提供一种标准化的方式,由本地或远程的用户或应用程序访问关系数据库。然而关系数据库厂商尽管支持SQL,但他们将自己专有的扩展加到了SQL中。这样使厂商能够让众多产品有所差别,但也产生了潜在的不兼容性。顾客工作站表示服务服务器中间件交互应用逻辑中间件中间件应用服务通信软件通信软件协议交互服务器操作系统顾客操作系统硬件平台硬件平台图4-4 在顾客/服务器结构中中间件的作用4.1.5 文件cache的一致性当使用文件服务器时,文件I/O的性能相对于局部文件访问具有显著的下降,主要原因是网络带来的延迟。为了减少这种性能下降,系统可以使用文件高速缓冲器来保存最近访问的文件记录。由于局部性原理,使用本地文件高速缓冲器可以减少必须进行的远程服务器访问次数。文件通道服务器通道顾客cache服务器cache磁盘通道磁盘通道顾客磁盘服务器磁盘图4-5 文件高速缓存机制图4-5描述了一种典型的文件高速缓存机制,用于在网络互连的工作站组上高速缓存文件。当进程要进行文件访问时,请求首先提交到进程所在的工作站的cache中,如果在那里未得到满足,则该请求或者传递给本地磁盘,或者传递给文件服务器。在服务器端,首先查询服务器上的cache,如果没有命中,则访问文件服务器的磁盘。双重高速缓存的方法用于减少通信量和磁盘I/O。当cache中总能含有远程数据的精确副本时,这些cache是一致的。Cache之间可能会变得不一致。这是因为远程数据已经改变,而相应的已经陈旧的本地cache副本并没有被废弃。当一个顾客修改了也被其它顾客高速缓存了的文件时,这种情况就会发生。这个问题实际上存在于两个层次上,如果顾客采用了将任何变化立即写回服务器的文件中的策略,则任何具有文件相关部分的cache副本的其它顾客将具有陈旧的数据。如果顾客延迟了将变化写回服务器,则问题就更糟了,因为服务器本身也只是拥有文件的旧版本,且将请求读至服务器的新文件也可能拥有陈旧的数据。保持本地cache副本是远程数据的最新变化的问题就是cache的一致性问题。解决cache一致性的最简单的方法是使用文件上锁技术,以防止多个顾客对文件的同时访问。这在损害性能和灵活性的前提下保证了一致性。Sprite系统中的机制提供了更好的方法,任何数目的远程进程可以打开一个文件,用于读入和生成它们自己的顾客cache,但是当一个针对服务器的打开文件请求要求写入访问而其它进程都是为读访问而打开这个文件的,则服务器要采取两步行动:第一,它告知写入进程,尽管它保留了一个cache,但是必须在发生更新时立即写回所有改变的块。系统在一个时刻最多只能有一个这样的顾客。第二,服务器告知所有使文件打开的读进程,该文件已不再是可缓存的了。4.1.6 服务员类型在本章所举的各个例子中都必须指定进程的类型(顾客/服务员)和协议的类型(面向连接/无连接),而对于服务员则还要进一步指出是并发型服务员还是反复型服务员(通常顾客并不在乎是和并发型服务员还是和反复型服务员通信)。这样,就给出4种可能组合如表4-1。表4-1服务员类型服务员类型反复型并发型面向连接不常用有代表性无连接有代表性不常用 4.2标准Internet服务和常见的应用4.2.1 标准Internet服务表4-2列出了TCP/IP多数实现都提供的一些标准服务。表中的所有服务同时使用TCP和UDP提供,并且这两个协议的端口号也相同。表4-2 标准TCP/IP服务名字TCP端口UDP端口RFC说明 Echo(回射)77862服务器返回顾客发送的数据Discard(丢弃)99863服务器丢弃顾客发送的数据Daytime(时间/日期)1313867服务器返回可读的日期和时间Chargen(字符生成)1919864TCP服务器发送连续的字符流,直到顾客终止连接。每当顾客发送一个数据报,UDP就返回一个包含随机数量字符的数据报。time(时间)3737868服务器返回一个32位二进制数值的时间。这个数值表示从1900年1月1日子时(UTC)以来所流逝的秒数。这些服务通常由Unix系统中的inetd守护进程提供。使用标准的telnet顾客程序很容易测试这些功能。这五个功能由inetd内部处理的功能,对于TCP版本的echo、discard和chargen服务器由inetd派生出来后作为子进程运行,这是因为它们都要运行到相应的顾客终止连接为止。另外两个TCP服务器time和daytime并不需要派生,因为它们的服务实现起来非常简单(取得当前的时间和日期,把它格式化后输出,在关闭连接),因此它们由inetd直接处理。所有五个UDP服务的处理都不需要fork调用,因为它们只就引发它们的顾客数据报生成并回应最多一个数据报,因此它们是由inetd直接处理的。4.2.2 常见的Internet应用表4-3总结了各种常见的Internet应用程序对协议的使用情况。表4-3 各种常见的Internet应用程序对协议的使用情况应用程序IPICMPUDPTCPPing*Traceroute*OSPF(路由协议)*RIP(路由协议)*BGP(路由协议)*BOOTP(引导协议)*DHCP(引导协议)*NTP(时间协议)*TFTP(简单的FTP)*SNMP(网络管理)*SMTP(电子邮件)*Telnet(虚拟终端)*FTP(文件传输)*HTTP(Web)*NNTP(网络新闻)*DNS(域名系统)*NFS(网络文件系统)*Sun RPC(远程过程调用)*表中,前两个应用程序Ping和Traceroute是诊断应用程序,它们使用ICMP协议。Traceroute构造自己的UDP分组来发送,并读ICMP的应答。紧接着是三个比较流行的路由协议,它们展示了路由协议使用的各种传输协议。OSPF采用原始套接口直接使用IP,而RIP使用UDP,BGB则使用TCP。下面5个是基于UDP的应用程序,接着5个应用程序使用TCP,最后3个是同时使用UDP和TCP的应用程序。4.3 并发服务器4.3.1 套接口对一个TCP连接的套接口对(socket pair)是一个定义该连接的两个端点的四元组:本地IP地址、本地TCP端口号、远程IP地址和远程TCP端口号。套接口对唯一标识一个互连网上的TCP连接。标识每个端点的两个值(IP地址和端口号)通常称为一个套接口。可以把套接口对的概念扩展到UDP,即使UDP是面向无连接的。当描述套接口函数(bind、connenct、getpeername等)时,注明它们在说明套接口对中那个值,例如bind函数要求应用程序说明本地IP和本地端口,既可以是TCP套接口,也可以是UDP套接口。4.3.2 并发服务器对于像时间/日期这样简单的服务器,使用迭代服务器(iterative server)具有较好的服务质量;但是,当顾客的请求需要长时间服务时,不可能让一个服务器长时间地为一个顾客服务,为了提供网络服务质量,而是同时为多个顾客服务。Unix系统下编写一个并发应用程序最简单的方法就是为每个顾客均fork一个子进程。下面代码给出了一个典型的并发服务器程序框架。 pid_t pid;int listenfd,connfd;listenfd=Socket(); /* create a socket */Bind(listenfd,);Listen(listenfd,LISTENQ); /* listen from the socket */For(;)Connfd=Accept(listenfd,);If(pid=Fork()=0) /* create a child process */Close(listenfd); /* child closes listening socket */Doit(connfd); Close(connfd);Exit(0); /* child terminates */Close(connfd); /* parent closes connected socket */并发服务器中主服务器循环派生子进程来处理每个新的连接。并发服务器让派生的子进程来处理顾客的请求。具体过程如图4-6所示。(监听套接口)*.21.*.*请求(1)服务器顾客Fork(2)服务器(子进程),1300,,21连接(3), 21,,1300(已连接套接口)图4-6 并发服务器让子进程处理顾客请求的服务图中有一个服务器,它的IP地址为。服务器在它的众所周知的端口(例如21端口)上执行被动打开,并等待顾客的请求。这里使用符号*.21.*.*指出服务器的套接口对。服务器在任意本地接口(第一个星号)的端口21上等待连接请求。远程IP地址和远程端口没有指定,用“*.*”表示。这里的星号称为通配(wildcard)符。如果运行服务器的主机有多个IP地址,服务器可以说明它只接受到达某个特定本地接口的外来连接。这是指定一个接口或者任意接口的选择。服务器不能指定多个地址的列表。通配的本地地址表示“任意”接口。此时,必须区别服务器的监听套接口和已连接套接口。这里,已连接套接口使用和监听套接口相同的端口号:21。同时,一旦建立连接,以连接的本地套接口的本地地址()随即填入。如果下一个顾客(假设IP地址为23,端口号为1301)随即也提出了服务请求,服务器就再派生第二个子进程对它进行服务,这时建立连接的四元组为, 21,23,1301。服务器能够区别这两个连接:第一个连接的套接口对和第二个连接的套接口对是不同的。通过上面的例子可以看出,TCP无法仅仅通过查看目的端口来分离外来的分组。它必须查看套接口对的所有四个元素才能确定由哪个端点接收到达的分组。对于上面的例子,对于端口21存在三个套接口,如果一个分组来自端口1300,目的地为端口 21,那么它就传送给第一个子进程;如果一个分组来自23端口1301,目的地为端口 21,那么它就传送给第二个子进程;所有其它的目的端口21的TCO分组则传送给拥有监听套接口的初始服务器(父进程)。4.3.3 缓冲区大小及其限制由于通信双方及中间节点的配置不同,有些会影响应用程序数据的传输。影响IP数据报大小的主要限制有:1. IPv4数据报的最大值为65535字节,包括IPv4的头部,总长度字段占16位。2. IPv6数据报的最大值为65575字节,包括40字节的IPv6头部,有效负载长度字段占16位。这里IPv6的有效负载长度字段不含IPv6头部,而IPv4的总长度字段包含IPv4头部。另外,IPv6有一个特大有效负载选项,它把有效负载长度字段扩展到32位,但是这个选项要求MTU超过65535的数据链路,这个选项是为主机到主机的内部连接而设计的,例如HIPPI,它们没有内在的MTU。3. 很多网络有一个最大传输单元MTU,它由硬件规定。例如以太网的MTU为1500字节。其它链路其MTU可以配置。IPv4要求的最小MTU是68字节,IPv6要求的最小MTU为576字节。4. 在两台主机间的路径上的最小MTU称为路径MTU。1500字节的以太网MTU是当今常见的路径MTU。路径MTU在不同方向可以不相同,因为在因特网中路由是非对称的。5. 当一个数据报将从某个接口发出时,如果它的大小超过相应链路的MTU,IPv4和IPV6都将执行分片(fragmentation)操作。各片段到达目的地前不会被重组(reassembling)。IPV4主机对其产生的数据报执行分片,IPV4路由器对其转发的数据报也执行分片。但是,IPv6只在数据报产生的主机执行分片;IPv6路由器对其转发的数据报不进行分片。事实上,IPv6路由器可以执行分片,但只对那些由路由器产生的数据报而不是转发的数据报。这时的路由器实际上作为主机运行。例如,大多数路由器支持Telnet协议,系统管理员就用它来配置路由器。由路由器的Telnet服务器产生的IP数据报是由路由器产生的,而不是路由器转发的。6. IPv4头部的DF(“不分片”)位若被设置,那么不管是发送主机还是转发路由器都不能对本数据报分片。当路由器接收到一个超过其外出链路MTU大小且设置了DF位的IPv4数据报时,它将产生一个ICMP的“destination unreachable, fragmentation needed but DF bit set(目的地不可达,需要分片但DF位已设置)”出错消息。由于IPv6路由器不执行分片,因此IPv6数据报隐含设置了DF位。如果IPv6路由器接收到一个超过其外出链路MTU大小的IPv6数据报年,它将产生一个ICMPv6的“packet too big(分组太大)”的出错消息。IPv4的DF位和IPv6的隐含DF位可用于路径MTU的发现。例如,如果TCP使用IPv4技术,它发送的数据报都将设置DF位。如果某个中间路由器返回一个ICMP的“destination unreachable, fragmentation needed but DF bit set“错误,TCP就减少每个数据报的数据量并重传。路径MTU的发现对IPv4是可选的,但所有IPv6的实现都必须支持它。7. IPv4和IPv6都定义了最小重组缓冲区大小:任何IPv4和IPv6的实现都必须支持的最小数据报大小。对IPv4和其值为576字节,对IPv6为1500字节。例如,对IPv4来说,如果不能确信给定的目的主机是否能接收577字节的数据报。所以很多使用UDP的应用程序(DNS、RIP、TFTP、BOOTP、SNMP)避免产生大于576字节的数据报。8. TCP有一个MSS(最大分片大小),用于向对方TCP通告在每个分片中能发送的最大TCP 数据量, 使用MSS的目的是告诉对方其重组缓冲区的实际值,从而避免分片。MSS经常设置成MTU减去IP和TCP头部的固定长度。在以太网环境下IPv4的MSS为1460字节,使用IPv6的MSS值为1440字节(两者的TCP头部都为20字节,而IPv4头部是20字节,IPv6的头部是40字节)。在TCP的MSS选项中,MSS值是一个16位字段,最大值是65535字节。这个值适合IPv4,因为IPv4数据报中的最大TCP数据量为65495字节(即65535减去IPv4头部20字节和TCP头部20字节)。但是,IPv6有特大有效负荷选项,因此需要使用另外的技术。首先,没有特大有效负荷选项的IPv6数据报中最大的TCP数据量为65515字节(即65535减去TCP头部20字节)。因此65535这个MSS值被认为是“无限”的特殊值,它只在用到特大有效负荷选项时才有用,而这种情况又要求MTU超过65535。如果TCP使用特大有效负荷选项,并且接收到的对方通告的MSS超过65535,那么它所发送数据报的大小限制就是接口MTU。如果这个值太大(即路径上某个链路的MTU比它小),那么路径MTU的发现功能将确定这个最小值。4.3.4 TCP发送图4-7说明了应用程序发送数据到TCP套接口的过程。用户进程应用进程缓冲区(大小任意)应用进程内核套接口发送缓冲区TCPMSS大小的TCP分片MSSMTU-40(IPv4)或-60(IPv6)IPMTU大小的IPv4或IPv6分组输出队列数据链路图4-7 应用程序发送数据到TCP套接口的过程每个TCP套接口都有一个发送缓冲区,用户可以使用SO_SNDBUF套接口选项来改变这个缓冲区的大小。当应用程序调用write时,内核从应用进程的缓冲区中拷贝所有数据到套接口的发送缓冲区。如果套接口的发送缓冲区不能存放应用程序的所有数据(即应用程序的缓冲区大于套接口发送缓冲区,或者套接口发送缓冲区还有其它数据),应用程序将被挂起(睡眠),这里假设套接口是阻塞的(缺省设置)。内核将不从write系统调用返回,直到应用进程缓冲区中的所有数据都拷贝到套接口发送缓冲区。所以从写一个TCP套接口的write调用成功返回仅表示可以重新使用应用进程的缓冲区。它并不能告诉用户对方的TCP或对方应用进程已接收到数据。TCP取出套接口发送缓冲区的数据并把它发送给对方TCP,其过程基于TCP数据传送的所有规则。对方TCP必须确认收到的数据,只有收到对方的ACK,发送方TCP才能删除套接口发送缓冲区中已确认的数据。TCP必须保留数据拷贝直到对方确认为止。TCP以MSS大小或者更小的块发送数据给IP(它同时给每个数据块填上TCP头部以构成分片),其中MSS是由对方通告的,当对方未通告时使用536这个值。IP给每个TCP分片填上IP头部以构成数据报,查找其目的IP地址的路由表项以确定外出接口,然后把数据报传递给相应的数据链路。IP可能先将数据报分段,再传送给链路层。但如上所述,MSS选项的目的是避免分片,而新的实现又使用路径MTU发现功能。每个链路有一个输出队列,如果输出队列满,则分组丢弃,并沿协议栈向上返回一个错误。TCP将注意这个错误,并在以后的某个时刻重传这个分片,但应用进程不知道这些细节。4.3.5 UDP发送图4-8说明了应用程序发送数据到UDP套接口的过程。用户进程应用进程缓冲区应用进程内核套接口发送缓冲区UDPUDP数据报 IPMTU大小的IPv4或IPv6分组输出队列数据链路图4-8 应用程序发送数据到UDP套接口的过程在图4-8中,套接口发送缓冲区用虚线框,因为它并不存在。UDP套接口有发送缓冲区的大小(用户可以使用SO_SNDBUF套接口选项修改),但是它仅仅是发送(sendto)到套接口的UDP数据报的上限。如果应用程序发送一个大于套接口发送缓冲区的数据报,则返回EMSGSIZE错误。由于UDP是一种不可靠的服务,它不必保存应用程序的数据拷贝,因此不需要一个真正的缓冲区。(应用进程的数据沿协议栈向下传递时,以某种形式拷贝到内核的缓冲区,当链路层把数据传出后这个拷贝将丢弃。)UDP简单地填加它的8个字节的头部以构成数据报并把它传递给IP。IPv4或IPv6给它填加相应的IP头部,执行路由操作确定外出接口,然后或者直接将数据报加入链路层输出队列(如果适合于MTU),或者分段后在把每个段加入数据链路层的输出队列。如果UDP应用进程发送一个大的数据报(例如2000字节数据报),它比TCP应用进程更有可能分片,因为TCP会将应用进程数据分成MSS大小的块,但UDP却没有这个能力。从写UDP套接口的sendto调用成功返回表示数据报或所有片段已被加入链路层的输出队列。如果输出队列没有足够的空间存放数据报或它的某个分片,UDP将返回应用进程ENOBUFS错误。这里需要注意的是,有些UDP的实现不返回这种错误,这样甚至数据报未经发出就丢失的情况应用进程也不知道。4.4 网络服务员工作模式当用户开发一个服务器程序时,有如下类型的进程控制方法可供选择:l 迭代服务器程序。这种方式主要用于简单的网络服务。它的主要缺点是在当前顾客服务完成之前,新到达的顾客无法得到服务。l 并发服务器程序,它为每个顾客fork一个子进程提供服务,这是Unix服务器程序通常的做法。l 使用select调用在一个进程内同时服务多个顾客的TCP服务器程序。l 使用线程替代进程实现的并发服务器程序。本节中介绍两种新的并发程序设计方法:l 预先派生子进程(preforking)。服务器启动后就派生一组子进程,形成一个子进程池。没当到来一个顾客请求,就从进程池内选择一个可用子进程为它服务。l 预先创建线程(prethreading)。服务器启动后就创建一组线程,形成一个线程池。每个顾客请求由池中的一个线程提供服务。 4.4.1 TCP迭代服务器程序迭代服务器总是在完全处理了一个顾客的请求后,才响应下一个顾客的请求。这种方式主要用于简单的网络服务,下面程序给出了使用迭代方法实现的一个简单的时间/日期服务器程序。#includeunp.h#includeintmain(int argc, char *argv)intlistenfd, connfd;struct sockaddr_inservaddr;charbuffMAXLINE;time_tticks;listenfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr);servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(13);/* daytime server */Bind(listenfd, (SA *) &servaddr, sizeof(servaddr);Listen(listenfd, LISTENQ);for ( ; ; ) connfd = Accept(listenfd, (SA *) NULL, NULL); ticks = time(NULL); snprintf(buff, sizeof(buff), %.24srn, ctime(&ticks); Write(connfd, buff, strlen(buff);Close(connfd);从进程控制的角度来看,迭代服务器是最快的,因为它不进行进程控制。4.4.2 TCP并发服务器程序传统上,并发服务器是调用fork派生一个子进程来处理顾客的请求。这使得服务器可在同一时间为多个顾客提供服务。唯一的限制是操作系统对同一用户可拥有的进程数量的限制。下面的程序例子是一个并发服务器的实现。绝大多数TCP服务器程序也是这样编写的。#includeunp.hintmain(int argc, char *argv)intlistenfd, connfd;pid_tchildpid;voidsig_chld(int), sig_int(int), web_child(int);socklen_tclilen, addrlen;struct sockaddr*cliaddr;if (argc = 2)listenfd = Tcp_listen(NULL, argv1, &addrlen);else if (argc = 3)listenfd = Tcp_listen(argv1, argv2, &addrlen);elseerr_quit(usage: serv01 );cliaddr = Malloc(addrlen);Signal(SIGCHLD, sig_chld);Signal(SIGINT, sig_int);for ( ; ; ) clilen = addrlen;if ( (connfd = accept(listenfd, cliaddr, &clilen) 0) if (errno = EINTR)continue;/* back to for() */elseerr_sys(accept error);if ( (childpid = Fork() = 0) /* child process */Close(listenfd);/* close listening socket */web_child(connfd);/* process the request */exit(0);Close(connfd);/* parent closes connected socket */* end serv01 */并发服务器的问题在于fork子进程时所消耗的CPU时间。20世纪80年代后期,当一个服务器一天只需处理几百、几千个顾客请求时,这样实现从性能上是能够满足的。然而到了Web时代,一个重负荷的Web服务器一天的访问数量以百万计。这种情况下,就单台主机而言,对于最繁忙的站点往往运行多台主机来分摊负载。改进服务器性能的方法有以下几种。可以采用预先派生子进程技术来提高并发服务器的性能。预先派生子进程服务器程序不再为每个顾客请求fork一个子进程,而是在服务器启动时就预先派生一组子进程,做好为接入的顾客请求服务的准备。图4-9显示了一个预先派生N个子进程的服务器正在为2个顾客同时服务的情形。顾客1子进程1顾客2子进程2父进程子进程3可用子进程池.子进程N图4-9 预先派生子进程的并发服务器使用预先派生子进程的描述代码如下:#includeunp.hstatic intnchildren;static pid_t*pids;intmain(int argc, char *argv)intlistenfd, i;socklen_taddrlen;voidsig_int(int);pid_tchild_make(int, int, int);if (argc = 3)listenfd = Tcp_listen(NULL, argv1, &addrlen);else if (argc = 4)listenfd = Tcp_listen(argv1, argv2, &addrlen);elseerr_quit(usage: serv02 );nchildren = atoi(argvargc-1);pids = Calloc(nchildren, sizeof(pid_t);for (i = 0; i nchildren; i+)pidsi = child_make(i, listenfd, addrlen);/* parent returns */Signal(SIGINT, sig_int);for ( ; ; )pause();/* everything done by children */这种技术的优点在于:不需要引入父进程执行fork的开销,新的顾客请求就能得到服务。而缺点在于:每次启动服务器时,父进程必须确定需要产生多少个子进程。如果不考虑再派生子进程,一旦所有子进程都被顾客请求占用,此时新到的请求将被暂时忽略,直到有一个子进程可用。对于父进程可以监视可用子进程数,一旦低于某个系统预先设定的阀值就再派生额外的子进程。同样,如果空闲子进程数大于某个阀值,则父进程将终止部分新派生的子进程。因为过多的子进程会占用系统的资源,从而导致系统性能下降。当系统负载较轻时,传统的并发服务器模型能够很好地处理顾客的请求,也就是每来一个请求,服务器就派生一个子进程为之服务。它甚至可以和inetd结合使用,由inetd负责接收每个连接。对于重负载情况,例如web服务器,可以使用相关的技术增强服务器的处理能力。4.4.3 4.4BSD上的实现在源自Berkeley的内核实现上,父进程在派生子进程之前创建监听套接口,而每次fork子进程时,各个子进程复制父进程的全部描述字。图4-10描述了proc结构(每个进程一个)、监听描述字的单个file结构以及单个socket结构的关系。在proc结构中,描述字只是某个数组的一个下标,用于引用一个file结构。而fork派生子进程时,子进程复制描述字的特性之一就是:子进程中给定的描述字所引用的file结构与父进程中同一描述字所引用的file结构一致。每个file结构有一个访问计数,它在文件或者套接口打开时为1,而每当调用fork或者dup本描述字时,它就增加1。在具有N个子进程的例子中,file结构的访问计数为N+1(父进程虽然从不调用accept,但它并未关闭该监听描述字)。当程序启动后,N个子进程被派生,它们分别调用accept并由内核置入睡眠状态。当第一个顾客连接到来时,N个睡眠进程均被唤醒。这是由于这N个进程共享一个socket结构,导致它们睡眠在同一等待通道(wait channel),即socket结构的so_timeo成员上。但是,虽然N个进程同时唤醒,只有最先被调度的进程才能获得顾客连接,而其它N-1个进程在执行过程中,它们会发现队列长度为0(连接已被取走),因此被再次投入睡眠。这通常被称为惊群(thundering herd)问题,因为尽管只有一个进程可以获得连接,但所有进程都被唤醒。这样虽然可以工作,但这种情况会导致系统性能的下降。为了避免惊群问题的发生,用户不希望有额外子进程空闲,某些Unix内核有一个名字为wake

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论