socket.socket(socket.AF_INET,socket.SOCK_STREAM)html
AF 表示ADDRESS FAMILY 地址族
PF 表示PROTOCOL FAMILY 协议族
但这两个宏定义是同样的
因此使用哪一个都没有关系
Winsock2.h中
#define AF_INET 0
#define PF_INET AF_INET
因此在windows中AF_INET与PF_INET彻底同样
而在Unix/Linux系统中,在不一样的版本中这二者有微小差异
对于BSD,是AF,对于POSIX是PF
UNIX系统支持AF_INET,AF_UNIX,AF_NS等,而DOS,Windows中仅支持AF_INET,它是网际网区域。
在函数socketpair与socket的domain参数中有AF_UNIX,AF_LOCAL,AF_INET,PF_UNIX,PF_LOCAL,PF_INET.
这几个参数有AF_UNIX=AF_LOCAL, PF_UNIX=PF_LOCAL, AF_LOCAL=PF_LOCAL, AF_INET=PF_INET.
建议:对于socketpair与socket的domain参数,使用PF_LOCAL系列,而在初始化套接口地址结构时,则使用AF_LOCAL.
例如: z = socket(PF_LOCAL, SOCK_STREAM, 0); adr_unix.sin_family = AF_LOCAL;
网络通讯的基本接口--socket,它扩展了操做系统的基本I/O到网络通讯,对不一样协议来讲是一种通用的接口,能够处理TCP和UDP通讯
Python 提供了两种利用socket发送和接收数据的方法:
socket对象:提供了send() senfto() recv() recvfrom()
文件类对象:提供了read() write() readline() (更适合TCP,和文件同样是以字节流形式运转)
socket模块定义了4中可能出现的异常:
与通常I/O和通讯问题有关的socket.error
与查询地址信息有关的socket.gaierror
与其余地址错误有关的socket.herror
与一个socket上调用socket.settimeout()后,处理超时有关的socket.timeout
一旦结束写操做,应该马上调用shutdown()函数,这样会强制清除缓存里面的内容,同时若是有任何问题就会产生一个异常
当使用文件对象 对makefile()调用 避免指定缓冲器,若是指定了,须要调用文件对象的flush()方法
UDP通讯几乎不使用文件类对象
#########
服务器 创建TCP链接的步骤:
1.创建socket对象
2.设置socket选项
s.setsocket(level,optname,value)
s.getsocket(level,optname[,buflen])
level为SOL_SOCKET(socket选型)的选项有(参考,对主机系统的依赖很高):
SO_BINDTODEVICE
SO_BROADCAST
SO_DONTROUTE
SO_KEEPALIVE:可使TCP通讯的信息包保持连续性,这些信息包能够在没有信息传输的时候,使通讯的双方肯定链接是保持的
SO_OOBINLINE
SO_REUSEADDR:当socket关闭后,本地端用于该socket的端口号马上就能够被重用,一般来讲只有通过系统定义的一段时间后,才能被重用
SO_TYPE
3.绑定到一个端口
4.侦听链接
s.listen(num) 这个调用告知操做系统准备接收链接,参数指明了服务器在实际链接时,容许有多少个未决(等待)链接在队列中等待
一般服务器连续运行来接收链接,能够设计一个无限循环。
一般状况下无限循环是很差的 由于他们会耗尽系统的cpu资源,然而 这里的循环是不一样的:当调用accept()的时候,它只在有一个客户端链接后才返回,同时,程序中止 并不使用任何
CPU资源。一个中止并等待输入或输出的程序称为被阻塞的程序
创建UDP服务器:没必要调用listen()和accept(),仅仅使用recvfrom()函数就能够了,该函数返回接收的数据和发送数据的地址
TCP服务器通常会用accept()来为每一个链接的客户端创建个新的socket,
UDP服务器只是使用一个单一的socket,并彻底依靠recvfrom()函数返回的值来判断往哪儿发送数据
flush() 是把缓冲区的数据强行输出, 主要用在IO中,即清空缓冲区数据,通常在读写流(stream)的时候,数据是先被读到了内存中,再把数据写到文件中,当你数据读完的时候不表明你的数据已经写完了,由于还有一部分有可能会留在内存这个缓冲区中。这时候若是你调用了close()方法关闭了读写流,那么这部分数据就会丢失,因此应该在关闭读写流以前先flush()。
避免死锁:
死锁发生在当一个服务器和客户端同时试图往一个链接上写东西和同时从一个链接上读的时候。
客户端 可使用多线程和其余方法,能够同时接收和发送
服务端 能够用超时 来摆脱死锁
DNS查询:
socket.getaddrinfo(host,port[,family[,socktype[,proto[,flags]]]])根据主机名查找IP
socket.gethostbyname()函数与IPV6不兼容
socket.gethostbyaddr(ip) 根据IP反向查找域名,须要确保为每个反向查找的行为捕获和处理socket.herror异常
socket.gethostname()获取操做系统配置中本地机器的主机名
“配置主机名两个地方都要改 不然本地获取socket.gethostbyname(socket.gethostname())会报错
/etc/sysconfig/network
/etc/hosts”python
socket.getfqdn(hostname) 根据主机名获取操做系统中完整的配置信息
不少系统是在私有网络上的,在公共的Internet上既得不到主机名,也得不到完整的名称
pydns模块 访问DNS系统的接口
DNS records DNS记录类型
初始化名称服务器 DNS.DiscoverNameServers()
创建请求对象 reqobj=DNS.Request() 这个对象用来发出任何DNS查询请求
执行实际查询 asobj=reqobj.req(name='www.baidu.com',qtype=DNS.Type.ANY) name实际查询的名称,qtype 指定DNS记录类型,返回一个包含结果的应答对象,其属性answers 包含全部返回的应答列表
******************************
DNS 相关术语
DNS A记录 NS记录 MX记录 CNAME记录 TXT记录 TTL值 PTR值 泛域名 泛解析 域名绑定 域名转向
1. DNS
DNS:Domain Name System 域名管理系统 域名是由圆点分开一串单词或缩写组成的,每个域名都对应一个唯一的IP地址,这一命名的方法或这样管理域名的系统叫作 域名管理系统。
DNS:Domain Name Server 域名服务器 域名虽然便于人们记忆,但网络中的计算机之间只能互相认识IP地址,它们之间的转换工做称为域名解析,域名解析须要由专门 的域名解析服务器来完成,DNS 就是进行域名解析的服务器。 查看DNS更详细的解释 linux
2. A记录
A (Address)记录是用来指定主机名(或域名)对应的IP地址记录。用户能够将该域名下的网站服务器指向到本身的web server上。同时也能够设置域名的子域名。通俗 来讲A记录就是服务器的IP,域名绑定A记录就是告诉DNS,当你输入域名的时候给你引导向设置在DNS的A记录所对应的服务器。 简单的说,A记录是指定域名对应的IP地址。 web
3. NS记录
NS(Name Server)记录是域名服务器记录,用来指定该域名由哪一个DNS服务器来进行解析。
您注册域名时,总有默认的DNS服务器,每一个注册的域名都是由一个DNS域名服务器来进行解析的,DNS服务器NS记录地址通常以如下的形式出现: ns1.domain.com、 ns2.domain.com等。
简单的说,NS记录是指定由哪一个DNS服务器解析你的域名。 数据库
4. MX记录 MX(Mail Exchanger)记录是邮件交换记录,它指向一个邮件服务器,用于电子邮件系统发邮件时根据收信人的地址后缀来定位邮件服务器。例如,当Internet 上的某用户要发一封信给 user@mydomain.com 时,该用户的邮件系统经过DNS查找mydomain.com这个域名的MX记录,若是MX记录存在, 用户计算机就将邮件发送 到MX记录所指定的邮件服务器上。 windows
5. CNAME记录
CNAME(Canonical Name )别名记录,容许您将多个名字映射到同一台计算机。一般用于同时提供WWW和MAIL服务的计算机。例如,有一台计算机名为
“host.mydomain.com”(A记录),它同时提供WWW和MAIL服务,为了便于用户访问服务。能够为该计算机设置两个别名(CNAME):WWW和MAIL, 这两个别名的 全称就“www.mydomain.com”和“mail.mydomain.com”,实际上他们都指向 “host.mydomain.com”。 api
6. TXT记录 缓存
TXT记录,通常指某个主机名或域名的说明,如:admin IN TXT "管理员, 电话:XXXXXXXXXXX",mail IN TXT "邮件主机,存放在xxx , 管理人:AAA", Jim IN TXT "contact: abc@mailserver.com",也就是您能够设置 TXT 内容以便使别人联系到您。
TXT的应用之一,SPF(Sender Policy Framework)反垃圾邮件。SPF是跟DNS相关的一项技术,它的内容写在DNS的TXT类型的记录里面。MX记录的做用是给寄信者指 明某个域名的邮件服务器有哪些。SPF的做用跟MX相反,它向收信者代表,哪些邮件服务器是通过某个域名承认会发送邮件的。SPF的做用主要是反垃圾邮件,主要针对那些 发信人伪造域名的垃圾邮件。例如:当邮件服务器收到自称发件人是spam@gmail.com的邮件,那么到底它是否是真的gmail.com的邮件服务器发过来的呢,咱们能够查询 gmail.com的SPF记录,以此防止别人伪造你来发邮件。 服务器
7.SOA记录网络
7.任何 DNS 记录文件(Domain Name System (DNS) Zone file)中, 都是以SOA(Start of Authority)记录开始。SOA 资源记录代表此 DNS 名称服务器是为 该 DNS 域中的数据的信息的最佳来源。SOA 记录与 NS 记录的区别:简单讲,NS记录表示域名服务器记录,用来指定该域名由哪一个DNS服务器来进行解析;SOA记录设置一 些数据版本和更新以及过时时间的信息.
7. TTL值
TTL(Time-To-Live)原理:TTL是IP协议包中的一个值,它告诉网络路由器包在网络中的时间是否太长而应被丢弃。有不少缘由使包在必定时间内不能被传递到目的地。例 如,不正确的路由表可能致使包的无限循环。一个解决方法就是在一段时间后丢弃这个包,而后给发送者一个报文,由发送者决定是否要重发。TTL的初值一般是系统缺省值, 是包头中的8位的域。TTL的最初设想是肯定一个时间范围,超过此时间就把包丢弃。因为每一个路由器都至少要把TTL域减一,TTL一般表示包在被丢弃前最多能通过的路由器 个数。当记数到0时,路由器决定丢弃该包,并发送一个ICMP报文给最初的发送者。
简单的说,TTL就是一条域名解析记录在DNS服务器中的存留时间。当各地的DNS服务器接受到解析请求时,就会向域名指定的NS服务器发出解析请求从而得到解析记录;在 得到这个记录以后,记录会在DNS服务器中保存一段时间,这段时间内若是再接到这个域名的解析请求,DNS服务器将再也不向NS服务器发出请求,而是直接返回刚才得到的记 录,而这个记录在DNS服务器上保留的时间,就是TTL值。
TTL值设置的应用:
一是增大TTL值,以节约域名解析时间,给网站访问加速。
通常状况下,域名的各类记录是极少更改的,极可能几个月、几年内都不会有什么变化。咱们彻底能够增大域名记录的TTL值让记录在各地DNS服务器中缓存的时间加长,这 样在更长的一段时间内,咱们访问这个网站时,本地ISP的DNS服务器就不须要向域名的NS服务器发出解析请求,而直接从缓存中返回域名解析记录。
二是减少TTL值,减小更换空间时的不可访问时间。
更换空间99.9%会有DNS记录更改的问题,由于缓存的问题,新的域名记录在有的地方可能生效了,但在有的地方可能等上一两天甚至更久才生效。结果就是有的人可能访问 到了新服务器,有的人访问到了旧服务器。仅仅是访问的话,这也不是什么大问题,但若是涉及到了邮件发送,这个就有点麻烦了,说不定哪封重要信件就被发送到了那已经 停掉的旧服务器上。
为了尽量的减少这个各地的解析时间差,合理的作法是: 第一步,先查看域名当前的TTL值,咱们假定是1天。
第二步,修改TTL值为可设定的最小值,可能的话,建议为1分钟,就是60。 第三步,等待一天,保证各地的DNS服务器缓存都过时并更新了记录。
第四步,设置修改新记录,这个时候各地的DNS就能以最快的速度更新到新的记录。
第五步,确认各地的DNS已经更新完成后,把TTL值设置成您想要的值。
通常操做系统的默认TTL值以下:
TTL=32 Windows 9x/Me
TTL=64 LINUX
TTL=128 Windows 200x/XP
TTL=255 Unix
8. PTR值
PTR是pointer的简写,用于将一个IP地址映射到对应的域名,也能够当作是A记录的反向,IP地址的反向解析。
PTR主要用于邮件服务器,好比邮箱AAA@XXX.com给邮箱BBB@yahoo.com发了一封邮件,yahoo邮件服务器接到邮件时会查看这封邮件的头文件,并分析是由哪一个IP地 址发出来的,而后根据这个IP地址进行反向解析,若是解析结果对应XXX.com的IP地址就接受这封邮件,反之则拒绝接收这封邮件。
9. 泛域名与泛解析
泛域名是指在一个域名根下,以 *.Domain.com的形式表示这个域名根全部未创建的子域名。
泛解析是把*.Domain.com的A记录解析到某个IP 地址上,经过访问任意的前缀.domain.com都能访问到你解析的站点上。
10. 域名绑定
域名绑定是指将域名指向服务器IP的操做。
11. 域名转向
域名转向又称为域名指向或域名转发,当用户地址栏中输入您的域名时,将会自动跳转到您所指定的另外一个域名。通常是使用短的好记的域名转向复杂难记的域名。
**********************
半开放socket:
socket是双向的,数据能够在socket上双向传送。
单向的socket 称为半开放socket
调用socket.shutdown()可使socket称为半开放的
参数:0,表示禁止未来读;1,禁止未来写;2,表示禁止未来读和写
一旦给出了关闭socket的方向,就不可逆,即不能在该方向上从新打开了,对shutdown()调用的效果是累计的
socket.settimeout(seconds)设置超时秒数,当访问一个socket时,若是通过参数设置的时间后,什么都没有发生后,就会产生一个socket.timeout异常
传输不定长字符串的数据结束标识:
惟一字符串结束标识符:发送方会在发送正文后附加一个字符串结束标识符号。这个标示符是一个NULL字符(python中是'\0')或newline字符(python中是'\n')
转义符(Escaping): 能够在字符串正文中包含字符串结束标示符
数据编码:这样字符串结束标识符就不会出现,但会增长传输文件的大小
可调整的字符串结束标示符
首部的大小指示器:
首先发送一个固定宽度的数字,表示要发送数据的长度,接收方会根据这个数字接收该长度的数据. 可是发送发必须在发送以前就知道要发送数据的字节数。
网络字节顺序:标准的网络传输二进制数据表示方法
python的struct模块提供了在python和二进制数据之间转换的支持
服务器绑定到127.0.0.1 意味着只接受本地机器的其余程序的连接。
poll()和select()实现事件通知(I/O 复用):
一般状况下,socket上的I/O是阻塞的
两个标准的工具:select() 和 poll(),他们均可以通知操做系统是哪一个socket对你的程序"感兴趣".当某个socket上有事件发生时,操做系统会通知你发生了什么,你就能够进行处理。
windows不支持poll(),必须使用select()(早期使用比较广泛,可是同时观察多个socket时,会变得很慢)
select.poll()调用返回一个poll对象,接着就能够把它用在须要观察的socket上
1 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 2 s.connect((host, port)) 3
4 p = select.poll() 5 p.register(s.fileno(), select.POLLIN | select.POLLERR | select.POLLHUP) 6 while 1: 7 results = p.poll(50) #参数可选,表示须要等待多少毫秒后,会发生某件事。若是什么都没发生,返回一个空的列表 8 if len(results): 9 if results[0][1] == select.POLLIN: 10 data = s.recv(4096) 11 if not len(data): 12 print("\rRemote end closed connection; exiting.") 13 break
14 # Only one item in here -- if there's anything, it's for us.
15 sys.stdout.write("\rReceived: " + data) 16 sys.stdout.flush() 17 else: 18 print "\rProblem occured; exiting."
19 sys.exit(0) 20 spin()#其余的处理事务
使用select()来解决I/O阻塞是经过调用一个函数来实现的
select(iwtd,owtd,ewtd[,timeout])
iwtd 一个为输入而观察的文件对象列表
owtd ...输出...
ewtd ...错误...
timeout 浮点类型,超时的秒数
返回3个tuple,每一个tuple都是一个准备好的列表
1 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 2 s.connect((host, port)) 3
4 while 1: 5 infds, outfds, errfds = select.select([s], [], [s], 0.05) 6 if len(infds): 7 # Normally, one would use something like "for fd in infds" here.
8 # We don't bother since there will only ever be a single file
9 # descriptor there.
10 data = s.recv(4096) 11 if not len(data): 12 print("\rRemote end closed connection; exiting.") 13 break
14 # Only one item in here -- if there's anything, it's for us.
15 sys.stdout.write("\rReceived: " + data) 16 sys.stdout.flush() 17 if len(errfds): 18 print "\rProblem occured; exiting."
19 sys.exit(0) 20 spin()
================
SSL:
socket内置的ssl对象, ssl = socket.ssl(s)
ssl对象只提供两个方法 :read()和write(),至关于socket的recv()和read()
和send方法同样,write()不能保证会把全部的请求数据都写出,但ssl对象的没有对象的sendall函数,
能够本身实现:
1 def sendall(s, buf): 2 byteswritten = 0 3 while byteswritten < len(buf): 4 byteswritten += s.write(buf[byteswritten:]) 5
6 ssl = socket.ssl(s) 7
8 sendall(ssl, "GET / HTTP/1.0\r\n\r\n")
SSL对象不提供一个readline()方法
pyOpenSSL:
对OpenSSL的绑定,
支持验证远程主机的证书
====================================
SocketServer 模块
SocketServer 提供了两种不一样的方法,解决同时处理多个请求的问题
1.提供 ThreadingMixIn 类 :使用python thread 来处理链接,它并不区分不一样的链接
2.提供 ForkingMixIn类 (仅支持UNIX):forking是为每个新来的链接开启一个新的进程,全部这些进程都是独立的
3.nonblocking(或asynchronous通讯) :(不支持)
SimpleHTTPServer 类扩展了BaseHTTPServer类,
SimpleHTTPServer.SimpleHTTPRequestHandler它能够提供当前工做目录下符合规则的文件,以及支持查找index.html ,访问目录
CGIHTTPServer.CGIHTTPRequestHandler 能够处理cgi脚本 py脚本
能够基于 SocketServer.StreamRequestHandler类 实现本身的协议
定义一个handle()方法,在链接到来的时候和链接准备好的时候,handle()会被调用
StreamRequestHandler 会完成rfile和wfile等初始化任务,这两个变量实质上是socket.makefile()创建的,结果是类文件对象,用于读写
SocketServer.StreamRequestHandler类 的基类SocketServer.BaseRequestHandler会初始化一些变量,这些变量包含了客户端和环境变量的一些信息。
request对象 和 client_address 其实是在 TCPserver类中 调用“self.socket.accept()”返回的客户端socket和 client_address客户端地址,而后传给处理类。
CGI 脚本的弊端:
实现处理CGI脚本的服务器(可参考CGIHTTPServer.CGIHTTPRequestHandler),一般会forking或产生一个新的进程来处理cgi程序,每次请求进来 都会从新这样调用,这样的设计虽然使CGI脚本具备语言和服务器中立的特性。
可是,每次启动脚本,操做系统都须要为它创建新的进程,这样对大流量的站点,会耗费大量性能.
诸如,像Apache 的mod_python ,mod_wsgi(新的服务器与app通讯的协议),这类的 都是在服务器里面内置了 一个python解释器模块,因此app脚本只是在服务器进程
启动被载入一次,而每次的请求 ,只是调用相应的app脚本 处理方法,而不是单独启动一个进程来执行,并且这样还能够在服务器启动装载app时,设置一些全局共享变量,如数据库链接等。
================
服务器 多任务处理,同时高效处理多个网络链接
forking:(只适用于UNIX平台) fork()原理与c中的fork() 函数同样
调用os.fork(),它返回两次,这个函数把子进程的进程ID返回给服务进程,还会把零值返回给子进程
在使用fork的程序中,每一个进程都会有本身的变量拷贝,在一个进程中改变改变量,不会影响其余进程。
用forking处理多个请求的弊端:
重复的文件描述符:一般只要父进程或子进程不用socket时, 就立刻关闭它
zombie进程:
fork()的语义是创建在父进程对找出子进程何时,以及如何终止感兴趣的假定上的。
父进程经过os.wait()或相似的调用来得到子进程的运行信息
在子进程终止和父进程调用wait()之间的这段时间,子进程被称为zombie进程。它中止了运行,可是内存结构还容许父进程执行wait()保持着。
若是父进程在子进程以前终止,子进程会一直执行,操做系统会把它们的父进程设置为init (进程1)来从新指定父进程。init进程就会负责清除zombie进程。
性能:linux 经过copy on write (写时拷贝技术)内存来实现 fork()
1.定义信号处理程序,使用os.wait()和os.waitpid()来搜索终止的子进程信息。(被称为收割reaping)
waitpid()返回一个进程的PID和退出信息的tuple,不然产生一个异常
time.sleep()有一种特殊状况,若是任意一个信号处理程序被调用,睡眠会被马上终止,而不是继续等待剩余的时间
2.使用轮询(poll): 按期搜集检查子进程
这个方法不包含信号处理程序,信号处理程序在有些操做系统上会引发I/O功能的问题
锁定:在forking程序中,锁定是用来控制多个进程存取文件最经常使用的方法,锁定能够保证同时只有一个进程执行某些操做。
fcntl.flock 对文件进行加锁,全部获得锁,最后必须都被释放,不然产生死锁,致使进程一致等待其余进程,一般在try...finally块后加上去除锁
错误处理:
有时os.fork()会由于 操做系统没有足够的内存,进程表没有空间,或者超过管理员设定的进程最大值等缘由失败,若是不检查错误,os.fork()的失败就会终止服务器程序
os.fork()要么返回两次,要么会由于错误产生异常。若是有错误将不会返回PID,并且程序根本不会结束fork
捕获异常关闭客户端链接
threading:
线程间通讯比进程之间通讯更容易,全部线程都有一样的全局地址空间,一个线程的改动会影响其余线程,因此确保线程间通讯不会互相干扰更加剧要。
python有两个多线程的模块 thread 和threading, thread 模块是实现线程的低级接口,threading 能够提供更高级的方法。
Python的threading模块提供了一个Lock对象,这个对象能够被用来同步访问代码。Lock对象含有两个方法:
acquire()和release(),acquire()负责取得一个锁。若是没有线程正持有锁,acquire方法会马上获得锁,不然须要等待锁被释放。在这两种状况下,一旦acquire()返回,调用它的线程就持有锁。
release()会释放一个锁,若是有其余的线程正等待这个锁(经过acquire()),当release()被调用的时候,它们中的一个线程就会被唤醒,也就是说,某个线程中的acquire()将返回
访问共享且缺少的资源:(客户端链接)
生产者 / 消费者 模式
Semaphore 同步对象:Semaphore也有acquire()和 release()方法,Semaphore含有一个初始化的计数器,每次调用release(),计数器就
增长一次,每次acquire()被调用,计数器就减1.若是计数器是0值的时候acquire()被调用 ,它就只有在计数器等于或大于1的状况才返回。
Queue 队列 模块
避免死锁
当两个或更多的线程再等待资源时会产生死锁,这种状况下它们的请求时不能获得知足的。
死锁是很难被发现的,避免死锁的原则:
必定要以一个固定的顺序来取得锁;
必定要按照与取得锁相反的顺序释放锁。
多线程服务器:
多数多线程服务器的体系结构:
主线程(Main Thread):负责侦听请求的线程,当它收到一个请求的时候,一个新的工做者线程(worker thread)会被创建起来,处理该客户端的请求。当客户端断开链接的时候,worker thread就会终止。
使用线程能够保证主线程是能够接受UNIX信号的惟一线程
使用线程池:
线程池被设计成一个线程同时只为一个客户服务,可是在服务结束以后并不终止。线程池中的线程要么是事先所有创建起来,要么是在
须要的时候被创建。
线程池一般有一个可使用的线程数上限。若是达到了这个上限,客户端若是有链接,那么就会出现错误
线程池一般包含如下几个部分:
一个主要的侦听线程来分派和接收客户端的链接;
一些工做者线程用来处理客户端的链接;
一个线程管理系统用来管理woker线程和处理那些意外终止的woker线程;
客户端使用多线程
threading.Condiction 对象有一个潜在的锁,一个线程调用该对象的wait(n)方法,这个方法会释放锁并等待另外一个线程调用notify(),有事情发生的时候给其余线程发信号
异步 通讯(asynchronous communication)
forking 和 thread,一次处理多个链接,都是使操做系统同时执行多重代码来实现的。
异步通讯并不一样时运行多个进程(或线程),只是运行一个进程,这个进程会监视各类链接,在它们之间转换,并按照须要为每个
链接提供服务。
为了实现异步通讯,须要一些新的特性,其中一个特性就是不用中止全部程序就能够处理网络数据。
常规方法中,例如recv(),只有数据被彻底从网络接收到,它才会返回,在这以前进程不能作任何事情
socket能够被设置成nonblocking方式,在这种方式下,若是一个操做不能马上执行,对send()和recv()调用会马上返回socket.error异常,进程就能够继续。
可是老是试图经过没有准备好的socket来发送和接收数据时低效的,
因此最好有操做系统来通知socket何时准备好,一般调用select()和poll()能够作这个事情。
一般你须要通知操做系统对哪些socket感兴趣,在一个或多个socket能够调用以前,对它们调用是暂停的;
而后能够发现哪些socket是准备好的,而后处理它们,并从新等待。
缺点:
全部异步的代码都有一个重要的特征,那就是任何被阻塞一段时间的代码都要被去掉,一般那些执行复杂运算或耗时操做的服务器(数据库服务器)一般不能使用彻底的异步。
异步通讯仅仅会由于新链接而增长不多的开销。这就使它适合那些仅须要少量服务器端处理,就能够处理多个链接的服务器(web 和FTP服务器)。
在某些方面编写异步的服务器比forking和多线程服务器复杂的多,必须本身维护不少状态信息,而不是经过操做系统来作。这对于诸如sendall()这类函数的调用必须彻底避免。
由于服务器包含在单一进程中,因此不用考虑锁定,死锁和同步的问题。但要考虑必须本身维护多少状态信息。
python中的asyncore和asynchat,能够用来编写异步程序,Twisted框架提供了更多用于异步服务器的库。
当poll函数返回一个tuple的列表,列表中的每个tuple对应一个链接。代表有些感兴趣的事情在该链接上发生。接下来的任务检查每个tuple决定作什么.
tuple包含一个socket的文件描述符和一个事件.
监控多个master socket
守护进程监听多个端口,当有链接到来的时候,它会启动一个能够处理该链接的程序。经过这种方法,一个进程能够处理许多socket.
采用polling和forking / threading 的“混合技术”
异步通讯提供了一种一次处理多个链接的方法,和forking threading不一样,异步通讯实际上不会使服务器同时运行不一样的代码。相反当有客户端到来的时候,它使用nonblocking的I/O 和polling来为它们提供服务。
异步 I / O 会处于一个主循环的中心,该循环等待事件的到来。当有事件发生时,例如数据能够被读取,或能够写数据----程序知道发生了什么,并执行相关的操做。poll()函数被设计成可以一次观察多个socket.