首页 > 搜索 > CSAN94模型,网络编程_weixin

CSAN94模型,网络编程_weixin

互联网 2021-01-27 19:24:25
在线算命,八字测算命理

cs架构

什么是客户端/服务器架构?

不同的人有不同的答案。这要看你问的是什么人,以及指的是软件系统还是硬件系统了。但是,有一点是共通的:服务器是一个软件或硬件,用于向一个或多个客户端(客户)提供所需要的“服务”。服务器存在的唯一目的就是等待客户的请求,给这些客户服务,然后再等待其他的请求。另一方面,客户连上一个(预先已知的)服务器,提出自己的请求,发送必要的数据,然后就等待服务器的完成请求或说明失败原因的反馈。服务器不停地处理外来的请求,而客户一次只能提出一个服务的请求,等待结果。然后结束这个事务。客户之后也可以再提出其他的请求,只是,这个请求会被视为另一个不同的事务了。图16-1展示了如今最常见的“客户端/服务器”结构。一个用户或客户端电脑通过因特网从服务器上取数据。这的确是一个客户端/服务器架构的系统,但还有更多类似的系统满足客户端/服务器架构。而且,客户端/服务器架构也可以像应用到软件上那样应用到计算机硬件上。

1.硬件的客户端/服务器架构打印(机)服务器是一个硬件服务器的例子。它们处理打印任务,并把任务发给相连的打印机(或其他打印设备)。这样的计算机一般是可以通过网络访问的,而且客户机器可以远程发送打印请求给它。另一个硬件服务器的例子是文件服务器。它们一般拥有大量的存储空间,客户可以远程访问。客户端计算机可以把服务器的磁盘映射到自己本地,就像本地磁盘一样使用它们。其中,Sun公司的NetworkFile System (NFS)是使用最为广泛的网络文件系统之一。如果你事实上已经映射了一个网络上的磁盘,但你却不知道它到底是本地的还是网络的,那客户端/服务器系统就很好的完成了它们的工作。其目的就是要让用户使用起来感觉就像使用本地磁盘一样。“抽象”到“一般的磁盘访问”这一层面之后,所有的操作就都是一样的了,而让所有操作都一样的“实现”则要依靠各自的程序了。2.软件客户端/服务器架构软件服务器也是运行在某个硬件上的。但不像硬件服务器那样,有专门的设备,如打印机、磁盘等。软件服务器提供的服务主要是程序的运行、数据的发送与接收、合并、升级或其他的程序或数据的操作。如今,最常用的软件服务器是Web服务器。一台机器里放一些网页或Web应用程序,然后启动服务。这样的服务器的任务就是接受客户端的请求,把网页发给客户端(如用户计算机上的浏览器),然后等待下一个客户端请求。这些服务启动后的目标就是“永远运行下去”。虽然它们不可能实现这样的目标,但只要没有关机或硬件出错等外力干扰,它们就能够运行非常长的一段时间。数据库服务器是另一种软件服务器。它们接受客户端的保存或读取请求,完成请求,然后再等待其他的请求。它们也被设计为要能“永远”运行。我们要讨论的最后一种软件服务器是窗口服务器。这些服务器几乎可以被认为是硬件服务器了。它们运行于一个有显示器的机器上。窗口客户端实际上是那些在运行时需要窗口环境的程序,它们一般叫做图形用户界面(GUI)程序。这些程序如果在一个DOS窗口或Unix的shell等没有窗口服务器的纯文本环境中运行,将无法启动。一旦窗口服务器可以使用时,那一切就正常了。当世界有了网络,那这样的环境就开始变得更有趣了。一般情况下,窗口客户端的显示和窗口服务器的提供都在同一台电脑上。但在X Window之类的网络化的窗口环境中,你可以选择其他电脑的窗口服务器来做显示。即你可以在一台电脑上运行GUI程序,而在另一台电脑上显示它!3.银行出纳是服务器吗理解客户端/服务器架构的一个方法是,想象一个不吃不喝不睡觉的银行出纳,他依次向排成长龙的顾客们提供一个又一个的服务(图16-2)。有时,队伍可能很长,有时也可能没人。但顾客随时都可能出现。当然,在以前,是不可能有这样的出纳的。但现在的ATM机与这个模型很像。

当然,出纳就是一个运行在无限循环里的服务器。每一个顾客就是一个想要得到服务的客户。顾客到了之后,就按先来先服务(first-come-first-served, FCFS)的原则得到服务。一个事务结束后,客户就离开了,而服务器则要么马上为下一个顾客服务,要么坐着等待下一个顾客的到来。为什么这些概念那么重要?因为,这种执行的方式就是客户端/服务器架构的特点。现在你对此已经有了大体的认识,我们就可以把客户端/服务器架构模型应用到网络编程中。出纳运行在一个接收请求,处理请求然后再处理其他请求或等待其他客户的无限循环中。客户有可能已经排起了长龙,也有可能根本就没有客户。但是,无论如何,服务器都不会结束工作。

在完成服务之前,服务器必需要先完成一些设置。先要创建一个通讯端点,让服务器能“监听”请求。你可以把我们的服务器比做一个公司的接待员或回答公司总线电话的话务员,一旦电话和设备安装完成,话务员也就位之后,服务就可以开始了。在网络世界里,基本上也是这样——一旦通信端点创建好之后,我们在“监听”的服务器就可以进入它那等待和处理客户请求的无限循环中了。当然,我们也不能忘记在信纸上、杂志里、广告中印上公司的电话号码。否则,就没有人会打电话进来了!同样地,服务器在准备好之后,也要通知潜在的客户,让它们知道服务器已经准备好处理服务了,否则没有人会提请求的。比方说,你建立了一个全新的网站。这个网站非常出色,非常的吸引人,非常的有用,是所有网站中最酷的一个。但如果你不把网站的网址或者说统一资源定位符(URL)广而告之的话,没有人会知道这个网站的存在的。这个网站也就永远不见天日了。对于公司总部的新电话也是这样,你不把电话公之于众,那就没有人会打电话进来。现在,你对服务器如何工作已经有了一个很好的认识。你已经完成了最难的那一部分。客户端的编程相对服务器端来说就简单得多了。所有的客户只要创建一个通信端点,建立到服务器的连接。然后客户端就可以提出请求了。请求中,也可以包含必要的数据交互。一旦请求处理完成,客户端收到了结果,通信就结束了。

 套接字

套接字是一种具有之前所说的“通信端点”概念的计算机网络数据结构。网络化的应用程序在开始任何通讯之前都必需要创建套接字。就像电话的插口一样,没有它就完全没办法通信。套接字起源于20世纪70年代加州大学伯克利分校版本的Unix,即人们所说的BSD Unix。因此,有时人们也把套接字称为“伯克利套接字”或“BSD套接字”。一开始,套接字被设计用在同一台主机上多个应用程序之间的通讯。这也被称作进程间通讯,或IPC。套接字有两种,分别是基于文件型的和基于网络型的。Unix套接字是我们要介绍的第一个套接字家族。其“家族名”为AF_UNIX(在POSIX1.g标准中也叫AF_LOCAL),表示“地址家族:UNIX”。包括Python在内的大多数流行平台上都使用术语“地址家族”及其缩写“AF”。而老一点的系统中,地址家族被称为“域”或“协议家族”,并使用缩写“PF”而不是“AF”。同样的,AF_LOCAL(在2000-2001年被列为标准)将会代替AF_UNIX。不过,为了向后兼容,很多系统上,两者是等价的。Python自己则仍然使用AF_UNIX。由于两个进程都运行在同一台机器上,而且这些套接字是基于文件的。所以,它们的底层结构是由文件系统来支持的。这样做相当有道理,因为,同一台电脑上,文件系统的确是不同的进程都能访问的。另一种套接字是基于网络的,它有自己的家族名字:AF_INET,或叫“地址家族:Internet”。还有一种地址家族AF_INET6被用于网际协议第6版(IPv6)寻址上。还有一些其他的地址家族,不过,它们要么是只用在某个平台上,要么就是已经被废弃,或是很少被使用,或是根本就还没有实现。所有地址家族中,AF_INET是使用最广泛的一个。Python 2.5中加入了一种Linux套接字的支持:AF_NETLINK(无连接(稍后讲解))套接字家族让用户代码与内核代码之间的IPC可以使用标准BSD套接字接口。而且,相对之前那些往操作系统中加入新的系统调用、proc文件系统支持或是“IOCTL”等复杂的方案来说,这种方法显得更为精巧,更为安全。

Python只支持AF_UNIX, AF_NETLINK,和AF_INET家族。由于我们只关心网络编程,所以在本章的大部分时候,我们都只用AF_INET。

如果把套接字比做电话的插口——即通信的最底层结构,那主机与端口就像区号与电话号码的一对组合。有了能打电话的硬件还不够,你还要知道你要打给谁,往哪打。一个因特网地址由网络通信所必需的主机与端口组成。而且不用说,另一端一定要有人在听才可以。否则,你就会听到熟悉的声音“对不起,您拨的是空号,请查询后再拨”。你在上网的时候,可能也见过类似的情况,如“不能连接该服务器。服务器无响应或不可达”。

合法的端口号范围为0~65535。其中,小于1024的端口号为系统保留端口。如果你所使用的是Unix操作系统,那么就可以通过/etc/services文件获得保留的端口号(及其对应的服务/协议和套接字类型)。

面向连接无论你使用哪一种地址家族,套接字的类型只有两种。一种是面向连接的套接字,即在通信之前一定要建立一条连接,就像跟朋友打电话时那样。这种通信方式也被称为“虚电路”或“流套接字”。面向连接的通信方式提供了顺序的、可靠的、不会重复的数据传输,而且也不会被加上数据边界。这也意味着,每一个要发送的信息,可能会被拆分成多份,每一份都会不多不少地正确到达目的地。然后被重新按顺序拼装起来,传给正在等待的应用程序。实现这种连接的主要协议就是传输控制协议(即TCP)。要创建TCP套接字就得在创建的时候指定套接字类型为SOCK_STREAM。TCP套接字采用SOCK_STREAM这个名字,表达了它作为流套接字的特点。由于这些套接字使用网际协议(IP)来查找网络中的主机,所以这样形成的整个系统,一般会由这两个协议(TCP和IP)名的组合来描述,即TCP/IP。

无连接与虚电路完全相反的是数据报型的无连接套接字。这意味着,无需建立连接就可以进行通讯。但这时,数据到达的顺序、可靠性及不重复性就无法保证了。数据报会保留数据边界,这就表示,数据是整个发送的,不会像面向连接的协议那样被先拆分成小块。使用数据报来传输数据就像邮政服务一样。邮件和包裹不一定会按它们发送的顺序到达。事实上,它们还有可能根本到达不了!而且,在网络中报文甚至会重复发送,这也增加了复杂性。既然数据报有这么多缺点,为什么还要使用它呢?(一定有能胜过流套接字的功能!)由于面向连接套接字要提供一些保证,以及要维持虚电路连接,这都是很重的额外负担。数据报没有这些负担,所以它更“便宜”。通常能提供更好的性能,更适合某些应用场合。

实现这种连接的主要协议就是用户数据报协议(即UDP)。要创建UDP套接字就得在创建的时候指定套接字类型为SOCK_DGRAM。 SOCK_DGRAM这个名字,也许你已经猜到了,来自于单词“datagram”(“数据报”)。由于这些套接字使用网际协议来查找网络中的主机,这样形成的整个系统,一般会由这两个协议(UDP和IP)名的组合来描述,即UDP/IP。

1 服务器端套接字函数 2 s.bind() 绑定地址(主机,端口号对)到套接字 3 s.listen() 开始 TCP 监听 4 s.accept() 被动接受 TCP 客户的连接,(阻塞式)等待连接的到来 5 6 客户端套接字函数 7 s.connect() 主动初始化 TCP 服务器连接 8 s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛异常 9 10 普通的套接字方法11 s.recv() 接收 TCP 消息12 s.recv_into()接收 TCP 消息到指定的缓冲区13 s.send() 发送 TCP 消息14 s.sendall() 完整地发送 TCP 消息15 s.recvfrom() 接收 UDP 消息16 s.recvfrom_into()接收 UDP 消息到指定的缓冲区17 s.sendto() 发送 UDP 消息18 s.getpeername() 连接到套接字(TCP)的远程地址19 s.getsockname() 当前套接字的地址20 s.getsockopt() 返回给定套接字选项的值21 s.setsockopt() 设置给定套接字选项的值22 s.shutdown() 关闭连接23 s.close() 关闭套接字24 s.detach()在未关闭文件描述符的情况下关闭套接字,返回文件描述符25 s.ioctl()控制套接字的模式(仅支持 Windows)26 27 面向阻塞的套接字方法28 s.setblocking() 设置套接字的阻塞或非阻塞模式29 s.settimeout() 设置阻塞套接字操作的超时时间30 s.gettimeout() 获取阻塞套接字操作的超时时间31 32 面向文件的套接字方法33 s.fileno() 套接字的文件描述符34 s.makefile() 创建与套接字关联的文件对象35 36 数据属性37 s.family套接字家族38 s.type套接字类型39 s.proto套接字协议 套接字函数

 

OSI参考模型

简单概述高低电平,电信号,0101但是单纯的0101没有意义,于是有了数据链路层,把0101分组,并且数据链路层有一个统一的以太网协议,Ethernet协议Ethernet协议规定l 一组电信号构成一个数据包,叫做“帧”l 每一帧数据分为报头head和数据data两部分 跨子网通信要用到IP地址,端口找到那个软件

 

而在应用层与传输层之间,规定了一个抽象层对于开发人员来说,只需要遵循socket协议开发,屏蔽底层细节socket相当于IP+port

family: AF_INET/AF_VNIXtype: SOCK_STREAM或SOCK_DGRAMproto一般不填

简单的socektserver

1 import socket 2 3 # stream 就是tcp协议,tcp叫流式协议 4 # dgram 就是报,udp协议 5 tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 6 tcp_server.bind(("127.0.0.1", 8080)) 7 tcp_server.listen(5) 8 conn, client_addr = tcp_server.accept() 9 print("client_addr: ", client_addr)10 print(client_addr)11 12 # 通讯循环13 while True:14 # 收消息15 client_msg = conn.recv(1024) # 收消息 8092-8K16 print("client_msg:", client_msg)17 # 发消息18 conn.send("lengmo".encode("utf-8"))19 20 conn.close()21 22 tcp_server.close() socket

防链接断开后抛异常

1 添加一个异常处理 2 import socket 3 # stream 就是tcp协议,tcp叫流式协议 4 # dgram 就是报,udp协议 5 tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 6 tcp_server.bind(("127.0.0.1", 8080)) 7 tcp_server.listen(5) 8 conn, client_addr = tcp_server.accept() 9 print("client_addr: ", client_addr)10 print(client_addr)11 # 通讯循环12 while True:13 try:14 # 收消息15 client_msg = conn.recv(1024) # 收消息 8092-8K16 print("client_msg:", client_msg)17 # 发消息18 conn.send("lengmo".encode("utf-8"))19 except Exception:20 break21 conn.close()22 tcp_server.close()23 24 也可以把整个循环try起来,finally后面加conn.close() 防止链接中断 1 importsocket 2 #stream就是tcp协议,tcp叫流式协议 3 #dgram就是报,udp协议 4 tcp_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 5 tcp_server.bind(("127.0.0.1",8080)) 6 tcp_server.listen(5) 7 8 9 whileTrue:10 conn,client_addr=tcp_server.accept()11 print("client_addr:",client_addr)12 #print(client_addr)13 #通讯循环14 while True:15 try:16 #收消息17 client_msg=conn.recv(1024)#收消息8092-8K18 print("client_msg:",client_msg)19 20 #发消息21 conn.send("lengmo".encode("utf-8"))22 exceptException:23 break24 25 conn.close()26 27 28 tcp_server.close() 循环建立链接

 

 

 

TCP可靠,三次握手四次挥手,底层每发一次消息都要确认UD更多用在广报消息,在意实时,不需要提前建立连接

 简单udp

 

粘包问题——现象

 

左边 server 右边 client左边 10 和 11 行期望收到的效果和打印出的效果不同,data1 和 data2 被放到一个文件夹中

 

 

发送端可以是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,也就是说,应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的。怎样定义消息呢?可以认为对方一次性write/send的数据为一个消息,需要明白的是当对方send一条信息的时候,无论底层怎样分段分片,TCP协议层会把构成整条消息的数据段排序完成后才呈现在内核缓冲区。例如基于tcp的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。1. TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。2. UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。3. tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头,实验略udp的recvfrom是阻塞的,一个recvfrom(x)必须对一个一个sendinto(y),收完了x个字节的数据就算完成,若是y>x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。两种情况下会发生粘包。发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包) 

Python struct模块

 

Python struct模块 用处struct模块中的函数格式化字符串 对齐方式格式符code使用示例Python参考手册struct模块链接

 

转载请标明出处(http://blog.csdn.net/lis_12/article/details/52777983).

用处 按照指定格式将Python数据转换为字符串,该字符串为字节流,如网络传输时,不能传输int,此时先将int转化为字节流,然后再发送;按照指定格式将字节流转换为Python指定的数据类型;处理二进制数据,如果用struct来处理文件的话,需要用’wb’,’rb’以二进制(字节流)写,读的方式来处理文件;处理c语言中的结构体; struct模块中的函数 函数returnexplainpack(fmt,v1,v2…)string按照给定的格式(fmt),把数据转换成字符串(字节流),并将该字符串返回.pack_into(fmt,buffer,offset,v1,v2…)None按照给定的格式(fmt),将数据转换成字符串(字节流),并将字节流写入以offset开始的buffer中.(buffer为可写的缓冲区,可用array模块)unpack(fmt,v1,v2…..)tuple按照给定的格式(fmt)解析字节流,并返回解析结果pack_from(fmt,buffer,offset)tuple按照给定的格式(fmt)解析以offset开始的缓冲区,并返回解析结果calcsize(fmt)size of fmt计算给定的格式(fmt)占用多少字节的内存,注意对齐方式 格式化字符串

当打包或者解包的时,需要按照特定的方式来打包或者解包.该方式就是格式化字符串,它指定了数据类型,除此之外,还有用于控制字节顺序、大小和对齐方式的特殊字符.

对齐方式

为了同c中的结构体交换数据,还要考虑c或c++编译器使用了字节对齐,通常是以4个字节为单位的32位系统,故而struct根据本地机器字节顺序转换.可以用格式中的第一个字符来改变对齐方式.定义如下

CharacterByte orderSizeAlignment@(默认)本机本机本机,凑够4字节=本机标准none,按原字节数大端标准none,按原字节数!network(大端)标准none,按原字节数

如果不懂大小端,见大小端参考网址.

格式符 格式符C语言类型Python类型Standard sizexpad byte(填充字节)no value ccharstring of length 11bsigned charinteger1Bunsigned charinteger1?_Boolbool1hshortinteger2Hunsigned shortinteger2iintinteger4I(大写的i)unsigned intinteger4l(小写的L)longinteger4Lunsigned longlong4qlong longlong8Qunsigned long longlong8ffloatfloat4ddoublefloat8schar[]string pchar[]string Pvoid *long 

注- -!

_Bool在C99中定义,如果没有这个类型,则将这个类型视为char,一个字节;q和Q只适用于64位机器;每个格式前可以有一个数字,表示这个类型的个数,如s格式表示一定长度的字符串,4s表示长度为4的字符串;4i表示四个int;P用来转换一个指针,其长度和计算机相关;f和d的长度和计算机相关; code,使用示例 1 #!/usr/bin/python2 # -*- coding:utf-8 -*-3 '''测试struct模块'''4 from struct import *5 import array6 7 def fun_calcsize():8 print 'ci:',calcsize('ci')#计算格式占内存大小9 print '@ci:',calcsize('@ci') 10 print '=ci:',calcsize('=ci') 11 print '>ci:',calcsize('>ci') 12 print 'ic:',calcsize('>ic') 17 print 'i',a)[0])#因为pack返回的是元组,即使只有一个元素也是元组的形式 65 print '小端:',hex(unpack('ci: 5 93 ic: 5 98 | UnixStreamServer | 37 +-----------++------------------+ 38 | 39 v 40 +-----------++--------------------+ 41 | UDPServer |------->| UnixDatagramServer | 42 +-----------++--------------------+ 43 44 Note that UnixDatagramServer derives from UDPServer, not from 45 UnixStreamServer -- the only difference between an IP and a Unix 46 stream server is the address family, which is simply repeated in both 47 unix server classes. 48 49 Forking and threading versions of each type of server can be created 50 using the ForkingMixIn and ThreadingMixIn mix-in classes.For 51 instance, a threading UDP server class is created as follows: 52 53 class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass 54 55 The Mix-in class must come first, since it overrides a method defined 56 in UDPServer! Setting the various member variables also changes 57 the behavior of the underlying server mechanism. 58 59 To implement a service, you must derive a class from 60 BaseRequestHandler and redefine its handle() method.You can then run 61 various versions of the service by combining one of the server classes 62 with your request handler class. 63 64 The request handler class must be different for datagram or stream 65 services.This can be hidden by using the request handler 66 subclasses StreamRequestHandler or DatagramRequestHandler. 67 68 Of course, you still have to use your head! 69 70 For instance, it makes no sense to use a forking server if the service 71 contains state in memory that can be modified by requests (since the 72 modifications in the child process would never reach the initial state 73 kept in the parent process and passed to each child).In this case, 74 you can use a threading server, but you will probably have to use 75 locks to avoid two requests that come in nearly simultaneous to apply 76 conflicting changes to the server state. 77 78 On the other hand, if you are building e.g. an HTTP server, where all 79 data is stored externally (e.g. in the file system), a synchronous 80 class will essentially render the service "deaf" while one request is 81 being handled -- which may be for a very long time if a client is slow 82 to read all the data it has requested.Here a threading or forking 83 server is appropriate. 84 85 In some cases, it may be appropriate to process part of a request 86 synchronously, but to finish processing in a forked child depending on 87 the request data.This can be implemented by using a synchronous 88 server and doing an explicit fork in the request handler class 89 handle() method. 90 91 Another approach to handling multiple simultaneous requests in an 92 environment that supports neither threads nor fork (or where these are 93 too expensive or inappropriate for the service) is to maintain an 94 explicit table of partially finished requests and to use select() to 95 decide which request to work on next (or whether to handle a new 96 incoming request).This is particularly important for stream services 97 where each client can potentially be connected for a long time (if 98 threads or subprocesses cannot be used). 99 100 Future work:101 - Standard classes for Sun RPC (which uses either UDP or TCP)102 - Standard mix-in classes to implement various authentication103 and encryption schemes104 - Standard framework for select-based multiplexing105 106 XXX Open problems:107 - What to do with out-of-band data?108 109 BaseServer:110 - split generic "request" functionality out into BaseServer class.111 Copyright (C) 2000Luke Kenneth Casson Leighton 112 113 example: read entries from a SQL database (requires overriding114 get_request() to return a table entry from the database).115 entry is processed by a RequestHandlerClass.116 117 """118 119 # Author of the BaseServer patch: Luke Kenneth Casson Leighton120 121 # XXX Warning!122 # There is a test suite for this module, but it cannot be run by the123 # standard regression test.124 # To run it manually, run Lib/test/test_socketserver.py.125 126 __version__ = "0.4"127 128 129 import socket130 import select131 import sys132 import os133 import errno134 try:135 import threading136 except ImportError:137 import dummy_threading as threading138 139 __all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer",140"ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler",141"StreamRequestHandler","DatagramRequestHandler",142"ThreadingMixIn", "ForkingMixIn"]143 if hasattr(socket, "AF_UNIX"):144 __all__.extend(["UnixStreamServer","UnixDatagramServer",145 "ThreadingUnixStreamServer",146 "ThreadingUnixDatagramServer"])147 148 def _eintr_retry(func, *args):149 """restart a system call interrupted by EINTR"""150 while True:151 try:152 return func(*args)153 except (OSError, select.error) as e:154 if e.args[0] != errno.EINTR:155 raise156 157 class BaseServer:158 159 """Base class for server classes.160 161 Methods for the caller:162 163 - __init__(server_address, RequestHandlerClass)164 - serve_forever(poll_interval=0.5)165 - shutdown()166 - handle_request()# if you do not use serve_forever()167 - fileno() -> int # for select()168 169 Methods that may be overridden:170 171 - server_bind()172 - server_activate()173 - get_request() -> request, client_address174 - handle_timeout()175 - verify_request(request, client_address)176 - server_close()177 - process_request(request, client_address)178 - shutdown_request(request)179 - close_request(request)180 - handle_error()181 182 Methods for derived classes:183 184 - finish_request(request, client_address)185 186 Class variables that may be overridden by derived classes or187 instances:188 189 - timeout190 - address_family191 - socket_type192 - allow_reuse_address193 194 Instance variables:195 196 - RequestHandlerClass197 - socket198 199 """200 201 timeout = None202 203 def __init__(self, server_address, RequestHandlerClass):实例参数最终传递到这里(ip与端口,自定义类)204 """Constructor.May be extended, do not override."""205 self.server_address = server_address206 self.RequestHandlerClass = RequestHandlerClass207 self.__is_shut_down = threading.Event()208 self.__shutdown_request = False209 210 def server_activate(self):211 """Called by constructor to activate the server.212 213 May be overridden.214 215 """216 pass217 218 def serve_forever(self, poll_interval=0.5):219 """Handle one request at a time until shutdown.220 221 Polls for shutdown every poll_interval seconds. Ignores222 self.timeout. If you need to do periodic tasks, do them in223 another thread.224 """225 self.__is_shut_down.clear()226 try:227 while not self.__shutdown_request:228 # XXX: Consider using another file descriptor or229 # connecting to the socket to wake this up instead of230 # polling. Polling reduces our responsiveness to a231 # shutdown request and wastes cpu at all other times.232 r, w, e = _eintr_retry(select.select, [self], [], [],233poll_interval)234 if self in r:235 self._handle_request_noblock()236 finally:237 self.__shutdown_request = False238 self.__is_shut_down.set()239 240 def shutdown(self):241 """Stops the serve_forever loop.242 243 Blocks until the loop has finished. This must be called while244 serve_forever() is running in another thread, or it will245 deadlock.246 """247 self.__shutdown_request = True248 self.__is_shut_down.wait()249 250 # The distinction between handling, getting, processing and251 # finishing a request is fairly arbitrary.Remember:252 #253 # - handle_request() is the top-level call.It calls254 # select, get_request(), verify_request() and process_request()255 # - get_request() is different for stream or datagram sockets256 # - process_request() is the place that may fork a new process257 # or create a new thread to finish the request258 # - finish_request() instantiates the request handler class;259 # this constructor will handle the request all by itself260 261 def handle_request(self):262 """Handle one request, possibly blocking.263 264 Respects self.timeout.265 """266 # Support people who used socket.settimeout() to escape267 # handle_request before self.timeout was available.268 timeout = self.socket.gettimeout()269 if timeout is None:270 timeout = self.timeout271 elif self.timeout is not None:272 timeout = min(timeout, self.timeout)273 fd_sets = _eintr_retry(select.select, [self], [], [], timeout)#调用select模块实现了多线程274 if not fd_sets[0]:275 self.handle_timeout()276 return277 self._handle_request_noblock()278 279 def _handle_request_noblock(self):280 """Handle one request, without blocking.281 282 I assume that select.select has returned that the socket is283 readable before this function was called, so there should be284 no risk of blocking in get_request().285 """286 try:287 request, client_address = self.get_request()288 except socket.error:289 return290 if self.verify_request(request, client_address):291 try:292 self.process_request(request, client_address)293 except:294 self.handle_error(request, client_address)295 self.shutdown_request(request)296 297 def handle_timeout(self):298 """Called if no new request arrives within self.timeout.299 300 Overridden by ForkingMixIn.301 """302 pass303 304 def verify_request(self, request, client_address):305 """Verify the request.May be overridden.306 307 Return True if we should proceed with this request.308 309 """310 return True311 312 def process_request(self, request, client_address):313 """Call finish_request.314 315 Overridden by ForkingMixIn and ThreadingMixIn.316 317 """318 self.finish_request(request, client_address)319 self.shutdown_request(request)320 321 def server_close(self):322 """Called to clean-up the server.323 324 May be overridden.325 326 """327 pass328 329 def finish_request(self, request, client_address):330 """Finish one request by instantiating RequestHandlerClass."""331 self.RequestHandlerClass(request, client_address, self)332 333 def shutdown_request(self, request):334 """Called to shutdown and close an individual request."""335 self.close_request(request)336 337 def close_request(self, request):338 """Called to clean up an individual request."""339 pass340 341 def handle_error(self, request, client_address):342 """Handle an error gracefully.May be overridden.343 344 The default is to print a traceback and continue.345 346 """347 print '-'*40348 print 'Exception happened during processing of request from',349 print client_address350 import traceback351 traceback.print_exc() # XXX But this goes to stderr!352 print '-'*40353 354 355 class TCPServer(BaseServer):ThreadingTCPServer基类之一,它又有自己的基类BaseServer"""Base class for various socket-based server classes.356 357 Defaults to synchronous IP stream (i.e., TCP).358 359 Methods for the caller:360 361 - __init__(server_address, RequestHandlerClass, bind_and_activate=True)362 - serve_forever(poll_interval=0.5)363 - shutdown()364 - handle_request()# if you don't use serve_forever()365 - fileno() -> int # for select()366 367 Methods that may be overridden:368 369 - server_bind()370 - server_activate()371 - get_request() -> request, client_address372 - handle_timeout()373 - verify_request(request, client_address)374 - process_request(request, client_address)375 - shutdown_request(request)376 - close_request(request)377 - handle_error()378 379 Methods for derived classes:380 381 - finish_request(request, client_address)382 383 Class variables that may be overridden by derived classes or384 instances:385 386 - timeout387 - address_family388 - socket_type389 - request_queue_size (only for stream sockets)390 - allow_reuse_address391 392 Instance variables:393 394 - server_address395 - RequestHandlerClass396 - socket397 398 """399 400 address_family = socket.AF_INET401 402 socket_type = socket.SOCK_STREAM403 404 request_queue_size = 5405 406 allow_reuse_address = False407 408 def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):409 """Constructor.May be extended, do not override."""410 BaseServer.__init__(self, server_address, RequestHandlerClass)调用它自己基类函数BaseServer的初始化函数进行多线程的启动411 self.socket = socket.socket(self.address_family,self.socket_type)创建启动的独立线程socket412 if bind_and_activate:413 try:414 self.server_bind()415 self.server_activate()416 except:417 self.server_close()418 raise419 420 def server_bind(self):421 """Called by constructor to bind the socket.422 423 May be overridden.424 425 """426 if self.allow_reuse_address:427 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)428 self.socket.bind(self.server_address)绑定地址429 self.server_address = self.socket.getsockname()430 def server_activate(self):431 """432 Called by constructor to activate the server. May be overridden.可以被重构433 """434 self.socket.listen(self.request_queue_size) 端口检听435 436def server_close(self):437 """Called to clean-up the server.438 May be overridden.439 """440 self.socket.close()关闭socket441 442 def fileno(self):443 """Return socket file number.444 445 Interface required by select().446 447 """448 return self.socket.fileno()449 450 def get_request(self):451 """Get the request and client address from the socket.452 May be overridden.453 """454 return self.socket.accept()455 456 def shutdown_request(self, request):457 """Called to shutdown and close an individual request."""458 try:459 #explicitly shutdown.socket.close() merely releases460 #the socket and waits for GC to perform the actual close.461 request.shutdown(socket.SHUT_WR)462 except socket.error:463 pass #some platforms may raise ENOTCONN here464 self.close_request(request)465 466 def close_request(self, request):467 """Called to clean up an individual request."""468 request.close()469 470 471 class UDPServer(TCPServer):472 473 """UDP server class."""474 475 allow_reuse_address = False476 477 socket_type = socket.SOCK_DGRAM478 479 max_packet_size = 8192480 481 def get_request(self):482 data, client_addr = self.socket.recvfrom(self.max_packet_size)483 return (data, self.socket), client_addr484 485 def server_activate(self):486 # No need to call listen() for UDP.487 pass488 489 def shutdown_request(self, request):490 # No need to shutdown anything.491 self.close_request(request)492 493 def close_request(self, request):494 # No need to close anything.495 pass496 497 class ForkingMixIn:498 499 """Mix-in class to handle each request in a new process."""500 501 timeout = 300502 active_children = None503 max_children = 40504 505 def collect_children(self):506 """Internal routine to wait for children that have exited."""507 if self.active_children is None:508 return509 510 # If we're above the max number of children, wait and reap them until511 # we go back below threshold. Note that we use waitpid(-1) below to be512 # able to collect children in size() syscalls instead513 # of size(): the downside is that this might reap children514 # which we didn't spawn, which is why we only resort to this when we're515 # above max_children.516 while len(self.active_children) >= self.max_children:517 try:518 pid, _ = os.waitpid(-1, 0)519 self.active_children.discard(pid)520 except OSError as e:521 if e.errno == errno.ECHILD:522 # we don't have any children, we're done523 self.active_children.clear()524 elif e.errno != errno.EINTR:525 break526 527 # Now reap all defunct children.528 for pid in self.active_children.copy():529 try:530 pid, _ = os.waitpid(pid, os.WNOHANG)531 # if the child hasn't exited yet, pid will be 0 and ignored by532 # discard() below533 self.active_children.discard(pid)534 except OSError as e:535 if e.errno == errno.ECHILD:536 # someone else reaped it537 self.active_children.discard(pid)538 539 def handle_timeout(self):540 """Wait for zombies after self.timeout seconds of inactivity.541 542 May be extended, do not override.543 """544 self.collect_children()545 546 def process_request(self, request, client_address):547 """Fork a new subprocess to process the request."""548 self.collect_children()549 pid = os.fork()550 if pid:551 # Parent process552 if self.active_children is None:553 self.active_children = set()554 self.active_children.add(pid)555 self.close_request(request) #close handle in parent process556 return557 else:558 # Child process.559 # This must never return, hence os._exit()!560 try:561 self.finish_request(request, client_address)562 self.shutdown_request(request)563 os._exit(0)564 except:565 try:566 self.handle_error(request, client_address)567 self.shutdown_request(request)568 finally:569 os._exit(1)570 571 572 class ThreadingMixIn:基类2573 """Mix-in class to handle each request in a new thread."""574 575 # Decides how threads will act upon termination of the576 # main process577 daemon_threads = False578 579 def process_request_thread(self, request, client_address):580 """Same as in BaseServer but as a thread.581 582 In addition, exception handling is done here.583 584 """585 try:586 self.finish_request(request, client_address)587 self.shutdown_request(request)588 except:589 self.handle_error(request, client_address)590 self.shutdown_request(request)591 592 def process_request(self, request, client_address):593 """Start a new thread to process the request."""594 t = threading.Thread(target = self.process_request_thread,595args = (request, client_address))596 t.daemon = self.daemon_threads597 t.start()598 599 600 class ForkingUDPServer(ForkingMixIn, UDPServer): pass601 class ForkingTCPServer(ForkingMixIn, TCPServer): pass602 603 class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass604 class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass605 606 if hasattr(socket, 'AF_UNIX'):607 608 class UnixStreamServer(TCPServer):609 address_family = socket.AF_UNIX610 611 class UnixDatagramServer(UDPServer):612 address_family = socket.AF_UNIX613 614 class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): pass615 616 class ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): pass617 618 class BaseRequestHandler:619 自定义MyServer自继承这里,主要用于处理来自每个线程的请求,函数拥有三个方法,但是每个方法的结构体都没pass,所以我们在继承以后需要对方法进行重构。620 """Base class for request handler classes.621 622 This class is instantiated for each request to be handled.The623 constructor sets the instance variables request, client_address624 and server, and then calls the handle() method.To implement a625 specific service, all you need to do is to derive a class which626 defines a handle() method.627 628 The handle() method can find the request as self.request, the629 client address as self.client_address, and the server (in case it630 needs access to per-server information) as self.server.Since a631 separate instance is created for each request, the handle() method632 can define arbitrary other instance variariables.633 """634 635 def __init__(self, request, client_address, server):接收请求request,客户端地址client_address,自定义server636 self.request = request分别赋值637 self.client_address = client_address638 self.server = server639 self.setup()首先执行setup()函数640 try:641 self.handle()再执行handle()642 finally:643 self.finish()最后执行finish()644 645 def setup(self):三个函数结构体都为pass ,需要在继承时进行方法重构646 pass647 648 def handle(self):649 pass650 651 def finish(self):652 pass653 654 655 # The following two classes make it possible to use the same service656 # class for stream or datagram servers.657 # Each class sets up these instance variables:658 # - rfile: a file object from which receives the request is read659 # - wfile: a file object to which the reply is written660 # When the handle() method returns, wfile is flushed properly661 662 663 class StreamRequestHandler(BaseRequestHandler):664 665 """Define self.rfile and self.wfile for stream sockets."""666 667 # Default buffer sizes for rfile, wfile.668 # We default rfile to buffered because otherwise it could be669 # really slow for large data (a getc() call per byte); we make670 # wfile unbuffered because (a) often after a write() we want to671 # read and we need to flush the line; (b) big writes to unbuffered672 # files are typically optimized by stdio even when big reads673 # aren't.674 rbufsize = -1675 wbufsize = 0676 677 # A timeout to apply to the request socket, if not None.678 timeout = None679 680 # Disable nagle algorithm for this socket, if True.681 # Use only when wbufsize != 0, to avoid small packets.682 disable_nagle_algorithm = False683 684 def setup(self):685 self.connection = self.request686 if self.timeout is not None:687 self.connection.settimeout(self.timeout)688 if self.disable_nagle_algorithm:689 self.connection.setsockopt(socket.IPPROTO_TCP,690socket.TCP_NODELAY, True)691 self.rfile = self.connection.makefile('rb', self.rbufsize)692 self.wfile = self.connection.makefile('wb', self.wbufsize)693 694 def finish(self):695 if not self.wfile.closed:696 try:697 self.wfile.flush()698 except socket.error:699 # An final socket error may have occurred here, such as700 # the local error ECONNABORTED.701 pass702 self.wfile.close()703 self.rfile.close()704 705 706 class DatagramRequestHandler(BaseRequestHandler):707 708 # XXX Regrettably, I cannot get this working on Linux;709 # s.recvfrom() doesn't return a meaningful client address.710 711 """Define self.rfile and self.wfile for datagram sockets."""712 713 def setup(self):714 try:715 from cStringIO import StringIO716 except ImportError:717 from StringIO import StringIO718 self.packet, self.socket = self.request719 self.rfile = StringIO(self.packet)720 self.wfile = StringIO()721 722 def finish(self):723 self.socket.sendto(self.wfile.getvalue(), self.client_address) SocketServer源码 服务器类型

5种类型:BaseServer,TCPServer,UnixStreamServer,UDPServer,UnixDatagramServer。 注意:BaseServer不直接对外服务。

服务器对象

class SocketServer.BaseServer:这是模块中的所有服务器对象的超类。它定义了接口,如下所述,但是大多数的方法不实现,在子类中进行细化。

BaseServer.fileno():返回服务器监听套接字的整数文件描述符。通常用来传递给select.select(), 以允许一个进程监视多个服务器。

BaseServer.handle_request():处理单个请求。处理顺序:get_request(), verify_request(), process_request()。如果用户提供handle()方法抛出异常,将调用服务器的handle_error()方法。如果self.timeout内没有请求收到, 将调用handle_timeout()并返回handle_request()。

BaseServer.serve_forever(poll_interval=0.5): 处理请求,直到一个明确的shutdown()请求。每poll_interval秒轮询一次shutdown。忽略self.timeout。如果你需要做周期性的任务,建议放置在其他线程。

BaseServer.shutdown():告诉serve_forever()循环停止并等待其停止。python2.6版本。

BaseServer.address_family: 地址家族,比如socket.AF_INET和socket.AF_UNIX。

BaseServer.RequestHandlerClass:用户提供的请求处理类,这个类为每个请求创建实例。

BaseServer.server_address:服务器侦听的地址。格式根据协议家族地址的各不相同,请参阅socket模块的文档。

BaseServer.socketSocket:服务器上侦听传入的请求socket对象的服务器。

服务器类支持下面的类变量:

BaseServer.allow_reuse_address:服务器是否允许地址的重用。默认为false ,并且可在子类中更改。

BaseServer.request_queue_size

请求队列的大小。如果单个请求需要很长的时间来处理,服务器忙时请求被放置到队列中,最多可以放request_queue_size个。一旦队列已满,来自客户端的请求将得到 “Connection denied”错误。默认值通常为5 ,但可以被子类覆盖。

BaseServer.socket_type:服务器使用的套接字类型; socket.SOCK_STREAM和socket.SOCK_DGRAM等。

BaseServer.timeout:超时时间,以秒为单位,或 None表示没有超时。如果handle_request()在timeout内没有收到请求,将调用handle_timeout()。

下面方法可以被子类重载,它们对服务器对象的外部用户没有影响。

BaseServer.finish_request():实际处理RequestHandlerClass发起的请求并调用其handle()方法。 常用。

BaseServer.get_request():接受socket请求,并返回二元组包含要用于与客户端通信的新socket对象,以及客户端的地址。

BaseServer.handle_error(request, client_address):如果RequestHandlerClass的handle()方法抛出异常时调用。默认操作是打印traceback到标准输出,并继续处理其他请求。

BaseServer.handle_timeout():超时处理。默认对于forking服务器是收集退出的子进程状态,threading服务器则什么都不做。

BaseServer.process_request(request, client_address) :调用finish_request()创建RequestHandlerClass的实例。如果需要,此功能可以创建新的进程或线程来处理请求,ForkingMixIn和ThreadingMixIn类做到这点。常用。

BaseServer.server_activate():通过服务器的构造函数来激活服务器。默认的行为只是监听服务器套接字。可重载。

BaseServer.server_bind():通过服务器的构造函数中调用绑定socket到所需的地址。可重载。

BaseServer.verify_request(request, client_address):返回一个布尔值,如果该值为True ,则该请求将被处理,反之请求将被拒绝。此功能可以重写来实现对服务器的访问控制。默认的实现始终返回True。client_address可以限定客户端,比如只处理指定ip区间的请求。 常用。

请求处理器

处理器接收数据并决定如何操作。它负责在socket层之上实现协议(i.e., HTTP, XML-RPC, or AMQP),读取数据,处理并写反应。可以重载的方法如下:

setup(): 准备请求处理. 默认什么都不做,StreamRequestHandler中会创建文件类似的对象以读写socket.

handle(): 处理请求。解析传入的请求,处理数据,并发送响应。默认什么都不做。常用变量:self.request,self.client_address,self.server。

finish(): 环境清理。默认什么都不做,如果setup产生异常,不会执行finish。

通常只需要重载handle。self.request的类型和数据报或流的服务不同。对于流服务,self.request是socket 对象;对于数据报服务,self.request是字符串和socket 。可以在子类StreamRequestHandler或DatagramRequestHandler中重载,重写setup()和finish() ,并提供self.rfile和self.wfile属性。 self.rfile和self.wfile可以读取或写入,以获得请求数据或将数据返回到客户端。

 

 

1 1 1 1

免责声明:非本网注明原创的信息,皆为程序自动获取互联网,目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责;如此页面有侵犯到您的权益,请给站长发送邮件,并提供相关证明(版权证明、身份证正反面、侵权链接),站长将在收到邮件12小时内删除。

相关阅读