网络编程

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_VNIX
type: SOCK_STREAM或SOCK_DGRAM
proto一般不填

简单的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-8K
16     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-8K
16         print("client_msg:", client_msg)
17         # 发消息
18         conn.send("lengmo".encode("utf-8"))
19     except Exception:
20         break
21 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-8K
18             print("client_msg:",client_msg)
19             
20             #发消息
21             conn.send("lengmo".encode("utf-8"))
22         exceptException:
23             break
24     
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模块

 

 

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

用处

  1. 按照指定格式将Python数据转换为字符串,该字符串为字节流,如网络传输时,不能传输int,此时先将int转化为字节流,然后再发送;
  2. 按照指定格式将字节流转换为Python指定的数据类型;
  3. 处理二进制数据,如果用struct来处理文件的话,需要用’wb’,’rb’以二进制(字节流)写,读的方式来处理文件;
  4. 处理c语言中的结构体;

struct模块中的函数

函数returnexplain
pack(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,按原字节数
> 大端 标准 none,按原字节数
! network(大端) 标准 none,按原字节数

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

格式符

格式符C语言类型Python类型Standard size
x pad byte(填充字节) no value  
c char string of length 1 1
b signed char integer 1
B unsigned char integer 1
? _Bool bool 1
h short integer 2
H unsigned short integer 2
i int integer 4
I(大写的i) unsigned int integer 4
l(小写的L) long integer 4
L unsigned long long 4
q long long long 8
Q unsigned long long long 8
f float float 4
d double float 8
s char[] string  
p char[] string  
P void * long  

注- -!

  1. _Bool在C99中定义,如果没有这个类型,则将这个类型视为char,一个字节;
  2. q和Q只适用于64位机器;
  3. 每个格式前可以有一个数字,表示这个类型的个数,如s格式表示一定长度的字符串,4s表示长度为4的字符串;4i表示四个int;
  4. P用来转换一个指针,其长度和计算机相关;
  5. f和d的长度和计算机相关;

code,使用示例

  1 #!/usr/bin/python
  2 # -*- coding:utf-8 -*-
  3 '''测试struct模块'''
  4 from struct import *
  5 import array
  6 
  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 '<ci:',calcsize('<ci')
 13     print 'ic:',calcsize('ic')#计算格式占内存大小
 14     print '@ic:',calcsize('@ic')
 15     print '=ic:',calcsize('=ic')
 16     print '>ic:',calcsize('>ic')
 17     print '<ic:',calcsize('<ic')
 18 
 19 def fun_pack(Format,msg = [0x11223344,0x55667788]):
 20     result = pack(Format,*msg)
 21     print 'pack'.ljust(10),str(type(result)).ljust(20),
 22     for i in result:
 23         print hex(ord(i)), # ord把ASCII码表中的字符转换成对应的整形,hex将数值转化为十六进制
 24     print
 25 
 26     result = unpack(Format,result)
 27     print 'unpack'.ljust(10),str(type(result)).ljust(20),
 28     for i in result:
 29         print hex(i),
 30     print 
 31 
 32 def fun_pack_into(Format,msg = [0x11223344,0x55667788]):
 33     r = array.array('c',' '*8)#大小为8的可变缓冲区,writable buffer
 34     result = pack_into(Format,r,0,*msg)
 35     print 'pack_into'.ljust(10),str(type(result)).ljust(20),
 36     for i in r.tostring():
 37         print hex(ord(i)),
 38     print
 39 
 40     result = unpack_from(Format,r,0)
 41     print 'pack_from'.ljust(10),str(type(result)).ljust(20),
 42     for i in result:
 43         print hex(i),
 44     print
 45 
 46 def IsBig_Endian():
 47     '''判断本机为大/小端'''
 48     a = 0x12345678
 49     result = pack('i',a)#此时result就是一个string字符串,字符串按字节同a的二进制存储内容相同。
 50     if hex(ord(result[0])) == '0x78':
 51         print '本机为小端'
 52     else:
 53         print '本机为大端'
 54 
 55 def test():
 56     a = '1234'
 57     for i in a:
 58         print '字符%s的二进制:'%i,hex(ord(i))#字符对应ascii码表中对应整数的十六进制
 59 
 60     '''
 61     不用unpack()返回的数据也是可以使用pack()函数的,只要解包的字符串符合解包格式即可,
 62     pack()会按照解包格式将字符串在内存中的二进制重新解释(说的感觉不太好...,见下例)
 63     '''
 64     print '大端:',hex(unpack('>i',a)[0])#因为pack返回的是元组,即使只有一个元素也是元组的形式
 65     print '小端:',hex(unpack('<i',a)[0])
 66 
 67 
 68 if __name__ == "__main__":
 69     print '判断本机是否为大小端?',
 70     IsBig_Endian()
 71 
 72     fun_calcsize()
 73 
 74     print '大端:'
 75     Format = ">ii"
 76     fun_pack(Format)
 77     fun_pack_into(Format)
 78 
 79     print '小端:'
 80     Format = "<ii"
 81     fun_pack(Format)
 82     fun_pack_into(Format)
 83 
 84     print 'test'
 85     test()
 86     '''
 87     result:
 88     判断本机是否为大小端? 本机为小端
 89     ci: 8
 90     @ci: 8
 91     =ci: 5
 92     >ci: 5
 93     <ci: 5
 94     ic: 5
 95     @ic: 5
 96     =ic: 5
 97     >ic: 5
 98     <ic: 5
 99     大端:
100     pack       <type 'str'>         0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88
101     unpack     <type 'tuple'>       0x11223344 0x55667788
102     pack_into  <type 'NoneType'>    0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88
103     pack_from  <type 'tuple'>       0x11223344 0x55667788
104     小端:
105     pack       <type 'str'>         0x44 0x33 0x22 0x11 0x88 0x77 0x66 0x55
106     unpack     <type 'tuple'>       0x11223344 0x55667788
107     pack_into  <type 'NoneType'>    0x44 0x33 0x22 0x11 0x88 0x77 0x66 0x55
108     pack_from  <type 'tuple'>       0x11223344 0x55667788
109     test
110     字符1的二进制: 0x31
111     字符2的二进制: 0x32
112     字符3的二进制: 0x33
113     字符4的二进制: 0x34
114     大端:0x31323334
115     小端:0x34333231
116     '''
View Code

 

Python参考手册struct模块链接

英文单词

英文中文
compact 紧凑的,简洁的
layout 布局
pad bytes 填充字节
alignment 对齐方式
maintain 维持,保持
proper 适当的
correspond 一致,相应的
platform-independent 平台依赖,即机器,操作系统不同,对齐方式,大小端等会有差异
omit 忽略
implicit 暗含的,隐式的
native 本台电脑的
presumably 假设
even if 即使
specify 指定
mechanism 原理,机制,手法
represented 表现,表示
assumed 假定的,默认的

socketserver实现并发

服务端的特点:
1. 一直提供服务(链接循环),基于一个链接通信循环
2. 绑定一个唯一的地址

拿到这个对象执行下面的handle方法

 

 

 

SocketServer

我们学习了利用socket模块创建socket通信服务,但细心学习后就会发现利用socket模块创建的服务无法进行多进程的处理,当需要进行大量请求处理时,请求就会阻塞在队列中,甚至发生请求丢弃。并且如果我们需要大量的socket时,就需要重复创建许多socket、绑定端口..... ,对于程序员来说意味着重复书写大量无意义代码。

那有没有一种方式既能简化书写流程又能实现多线程开发呢 ? 答案是肯定的,这就是SocketServer模块。

SocketServer简化了网络服务器的编写。在进行socket创建时,使用SocketServer会大大减少创建的步骤,并且SocketServer使用了select它有4个类:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。这4个类是同步进行处理的,另外通过ForkingMixIn和ThreadingMixIn类来支持异步。

使用SocketServer的步骤简介

1. 创建服务器的步骤。首先,你必须创建一个请求处理类,它是BaseRequestHandler的子类并重载其handle()方法。

2. 实例化一个服务器类,传入服务器的地址和请求处理程序类。

3. 最后,调用handle_request()(一般是调用其他事件循环或者使用select())或serve_forever()。

集成ThreadingMixIn类时需要处理异常关闭。daemon_threads指示服务器是否要等待线程终止,要是线程互相独立,必须要设置为True,默认是False。

无论用什么网络协议,服务器类有相同的外部方法和属性。

该模块在python3中已经更名为socketserver。

举例:

复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import SocketServer

class MyServer(SocketServer.BaseRequestHandler):
    def handle(self):
     //some method....
if __name__='__main__': server = SocketServer.ThreadingTCPServer(('127.0.0.1,9999'),MyServer) server.serve_forever()
复制代码

上面的步骤你可能会看不懂或者不理解为什么这么操作,下面我们进行详细解释。我们进行了如下的操作

一、自定义了一个MyServer类,继承自SocketServer模块中的BaseRequestHandler类。

二、在主函数中,使用SocketServer函数中的ThreadingTCPServer类进行了实例化操作。上边例子中实例化了对象为server,并在进行实例化时进行了参数的传递,参数一:服务器IP与端口号   参数二:自定义函数名称

源码分析

第二步中主函数操作。查看SocketServer模块源码[下面]可以发现。ThreadingTCPServer是继承自基类(ThreadingMixIn,TCPServer),但函数结构体是pass,也就是左右操作全部通过基类中方法进行执行,而基类中的TCPServer又有基类BaseServer。结构图如下

BaseServer   
  
TCPServer        ThreadingMixIn
    ↑                  ↑
     ThreadingTCPServer            

可以看出:ThreadingTCPServer的所有的方法都在它的基类函数中

各个基类作用分别是:

BaseServer:利用select创建多进程

TCPServer:创建每个进程的socket

ThreadingMixIn:Mix-in class to handle each request in a new thread.

第一步中的创建自定义类。继承自BaseRequestHandler类。从源码中看出他的作用就是接受请求,地址,和自定义名称,然后交给它的方法处理。默认的三个处理函数为pass,所以当我们使用时需要进行函数代码重构。

复制代码
class BaseRequestHandler:

    """Base class for request handler classes.

    This class is instantiated for each request to be handled.  The
    constructor sets the instance variables request, client_address
    and server, and then calls the handle() method.  To implement a
    specific service, all you need to do is to derive a class which
    defines a handle() method.

    The handle() method can find the request as self.request, the
    client address as self.client_address, and the server (in case it
    needs access to per-server information) as self.server.  Since a
    separate instance is created for each request, the handle() method
    can define arbitrary other instance variariables.

    """

    def __init__(self, request, client_address, server):
        self.request = request
        self.client_address = client_address
        self.server = server
        self.setup()
        try:
            self.handle()
        finally:
            self.finish()

    def setup(self):
        pass

    def handle(self):
        pass

    def finish(self):
        pass
复制代码
  1 """Generic socket server classes.
  2 This module tries to capture the various aspects of defining a server:
  3 For socket-based servers:
  4 - address family:
  5         - AF_INET{,6}: IP (Internet Protocol) sockets (default)
  6         - AF_UNIX: Unix domain sockets
  7         - others, e.g. AF_DECNET are conceivable (see <socket.h>
  8 - socket type:
  9         - SOCK_STREAM (reliable stream, e.g. TCP)
 10         - SOCK_DGRAM (datagrams, e.g. UDP)
 11 
 12 For request-based servers (including socket-based):
 13 
 14 - client address verification before further looking at the request
 15         (This is actually a hook for any processing that needs to look
 16          at the request before anything else, e.g. logging)
 17 - how to handle multiple requests:
 18         - synchronous (one request is handled at a time)
 19         - forking (each request is handled by a new process)
 20         - threading (each request is handled by a new thread)
 21 
 22 The classes in this module favor the server type that is simplest to
 23 write: a synchronous TCP/IP server.  This is bad class design, but
 24 save some typing.  (There's also the issue that a deep class hierarchy
 25 slows down method lookups.)
 26 
 27 There are five classes in an inheritance diagram, four of which represent
 28 synchronous servers of four types:
 29 
 30         +------------+
 31         | BaseServer |
 32         +------------+
 33               |
 34               v
 35         +-----------+        +------------------+
 36         | TCPServer |------->| 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 authentication
103   and encryption schemes
104 - Standard framework for select-based multiplexing
105 
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) 2000  Luke Kenneth Casson Leighton <lkcl@samba.org>
112 
113   example: read entries from a SQL database (requires overriding
114   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 Leighton
120 
121 # XXX Warning!
122 # There is a test suite for this module, but it cannot be run by the
123 # standard regression test.
124 # To run it manually, run Lib/test/test_socketserver.py.
125 
126 __version__ = "0.4"
127 
128 
129 import socket
130 import select
131 import sys
132 import os
133 import errno
134 try:
135     import threading
136 except ImportError:
137     import dummy_threading as threading
138 
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                 raise
156 
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_address
174     - 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 or
187     instances:
188 
189     - timeout
190     - address_family
191     - socket_type
192     - allow_reuse_address
193 
194     Instance variables:
195 
196     - RequestHandlerClass
197     - socket
198 
199     """
200 
201     timeout = None
202 
203     def __init__(self, server_address, RequestHandlerClass):      实例参数最终传递到这里(ip与端口,自定义类)
204         """Constructor.  May be extended, do not override."""
205         self.server_address = server_address        
206         self.RequestHandlerClass = RequestHandlerClass
207         self.__is_shut_down = threading.Event()
208         self.__shutdown_request = False
209 
210     def server_activate(self):
211         """Called by constructor to activate the server.
212 
213         May be overridden.
214 
215         """
216         pass
217 
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. Ignores
222         self.timeout. If you need to do periodic tasks, do them in
223         another thread.
224         """
225         self.__is_shut_down.clear()
226         try:
227             while not self.__shutdown_request:
228                 # XXX: Consider using another file descriptor or
229                 # connecting to the socket to wake this up instead of
230                 # polling. Polling reduces our responsiveness to a
231                 # shutdown request and wastes cpu at all other times.
232                 r, w, e = _eintr_retry(select.select, [self], [], [],
233                                        poll_interval)
234                 if self in r:
235                     self._handle_request_noblock()
236         finally:
237             self.__shutdown_request = False
238             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 while
244         serve_forever() is running in another thread, or it will
245         deadlock.
246         """
247         self.__shutdown_request = True
248         self.__is_shut_down.wait()
249 
250     # The distinction between handling, getting, processing and
251     # finishing a request is fairly arbitrary.  Remember:
252     #
253     # - handle_request() is the top-level call.  It calls
254     #   select, get_request(), verify_request() and process_request()
255     # - get_request() is different for stream or datagram sockets
256     # - process_request() is the place that may fork a new process
257     #   or create a new thread to finish the request
258     # - finish_request() instantiates the request handler class;
259     #   this constructor will handle the request all by itself
260 
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 escape
267         # handle_request before self.timeout was available.
268         timeout = self.socket.gettimeout()
269         if timeout is None:
270             timeout = self.timeout
271         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             return
277         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 is
283         readable before this function was called, so there should be
284         no risk of blocking in get_request().
285         """
286         try:
287             request, client_address = self.get_request()
288         except socket.error:
289             return
290         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         pass
303 
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 True
311 
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         pass
328 
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         pass
340 
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 '-'*40
348         print 'Exception happened during processing of request from',
349         print client_address
350         import traceback
351         traceback.print_exc() # XXX But this goes to stderr!
352         print '-'*40
353 
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_address
372     - 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 or
384     instances:
385 
386     - timeout
387     - address_family
388     - socket_type
389     - request_queue_size (only for stream sockets)
390     - allow_reuse_address
391 
392     Instance variables:
393 
394     - server_address
395     - RequestHandlerClass
396     - socket
397 
398     """
399 
400     address_family = socket.AF_INET
401 
402     socket_type = socket.SOCK_STREAM
403 
404     request_queue_size = 5
405 
406     allow_reuse_address = False
407 
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)      创建启动的独立线程socket
412         if bind_and_activate:
413             try:
414                 self.server_bind()
415                 self.server_activate()
416             except:
417                 self.server_close()
418                 raise
419 
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 
436  def server_close(self):
437         """Called to clean-up the server.
438         May be overridden.
439         """
440         self.socket.close()                              关闭socket
441 
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 releases
460             #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 here
464         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 = False
476 
477     socket_type = socket.SOCK_DGRAM
478 
479     max_packet_size = 8192
480 
481     def get_request(self):
482         data, client_addr = self.socket.recvfrom(self.max_packet_size)
483         return (data, self.socket), client_addr
484 
485     def server_activate(self):
486         # No need to call listen() for UDP.
487         pass
488 
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         pass
496 
497 class ForkingMixIn:
498 
499     """Mix-in class to handle each request in a new process."""
500 
501     timeout = 300
502     active_children = None
503     max_children = 40
504 
505     def collect_children(self):
506         """Internal routine to wait for children that have exited."""
507         if self.active_children is None:
508             return
509 
510         # If we're above the max number of children, wait and reap them until
511         # we go back below threshold. Note that we use waitpid(-1) below to be
512         # able to collect children in size(<defunct children>) syscalls instead
513         # of size(<children>): the downside is that this might reap children
514         # which we didn't spawn, which is why we only resort to this when we're
515         # 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 done
523                     self.active_children.clear()
524                 elif e.errno != errno.EINTR:
525                     break
526 
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 by
532                 # discard() below
533                 self.active_children.discard(pid)
534             except OSError as e:
535                 if e.errno == errno.ECHILD:
536                     # someone else reaped it
537                     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 process
552             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 process
556             return
557         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:          基类2
573     """Mix-in class to handle each request in a new thread."""
574 
575     # Decides how threads will act upon termination of the
576     # main process
577     daemon_threads = False
578 
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,
595                              args = (request, client_address))
596         t.daemon = self.daemon_threads
597         t.start()
598 
599 
600 class ForkingUDPServer(ForkingMixIn, UDPServer): pass
601 class ForkingTCPServer(ForkingMixIn, TCPServer): pass
602 
603 class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
604 class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
605 
606 if hasattr(socket, 'AF_UNIX'):
607 
608     class UnixStreamServer(TCPServer):
609         address_family = socket.AF_UNIX
610 
611     class UnixDatagramServer(UDPServer):
612         address_family = socket.AF_UNIX
613 
614     class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): pass
615 
616     class ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): pass
617 
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.  The
623     constructor sets the instance variables request, client_address
624     and server, and then calls the handle() method.  To implement a
625     specific service, all you need to do is to derive a class which
626     defines a handle() method.
627 
628     The handle() method can find the request as self.request, the
629     client address as self.client_address, and the server (in case it
630     needs access to per-server information) as self.server.  Since a
631     separate instance is created for each request, the handle() method
632     can define arbitrary other instance variariables.
633     """
634 
635     def __init__(self, request, client_address, server):    接收请求request,客户端地址client_address,自定义server
636         self.request = request                   分别赋值
637         self.client_address = client_address
638         self.server = server
639         self.setup()                         首先执行setup()函数
640         try:
641             self.handle()                      再执行handle()
642         finally:
643             self.finish()                      最后执行finish()
644 
645     def setup(self):              三个函数结构体都为pass ,需要在继承时进行方法重构
646         pass
647 
648     def handle(self):
649         pass
650 
651     def finish(self):
652         pass
653 
654 
655 # The following two classes make it possible to use the same service
656 # 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 read
659 # - wfile: a file object to which the reply is written
660 # When the handle() method returns, wfile is flushed properly
661 
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 be
669     # really slow for large data (a getc() call per byte); we make
670     # wfile unbuffered because (a) often after a write() we want to
671     # read and we need to flush the line; (b) big writes to unbuffered
672     # files are typically optimized by stdio even when big reads
673     # aren't.
674     rbufsize = -1
675     wbufsize = 0
676 
677     # A timeout to apply to the request socket, if not None.
678     timeout = None
679 
680     # Disable nagle algorithm for this socket, if True.
681     # Use only when wbufsize != 0, to avoid small packets.
682     disable_nagle_algorithm = False
683 
684     def setup(self):
685         self.connection = self.request
686         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,
690                                        socket.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 as
700                 # the local error ECONNABORTED.
701                 pass
702         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 StringIO
716         except ImportError:
717             from StringIO import StringIO
718         self.packet, self.socket = self.request
719         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

posted @ 2018-02-17 19:04  xiesibo  阅读(283)  评论(0编辑  收藏  举报