PYTHON網絡編程筆記

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.

相關文章
相關標籤/搜索