使用某某代理時免費版的限制鏈接數,除此就要購買......實現個簡單的代理服務程序,一探究竟,git
固然複雜的也是這些原理。代理服務原理很簡單,就拿瀏覽器與web服務器來講。無非是A瀏覽器github
發request給B代理,B代理再把request把送給C web服務,而後C的reponse->B->A。web
要寫web代理服務就要先了解下http協議,固然並不要多深刻,除非要實現強大的功能:修改XX信息、chrome
負載均衡等。http請求由三部分組成:請求行、消息報頭、請求正文;瀏覽器
詳細的網上有,想了解能夠看看。下面是一個正常的GET請求頭(Cookie部分本人沒截屏,使用的系統w7):服務器
能夠看到首行:GET是請求方法, /是路徑,在後面是協議版本;第二行之後是請求報頭,都是鍵值對形式;多線程
GET方法沒有正文。post有正文,除此以外,請求方法頭部基本一致,每一行結尾都是\r\n。負載均衡
基本的請求方法,以下:
GET 請求獲取Request-URI所標識的資源
POST 在Request-URI所標識的資源後附加新的數據
HEAD 請求獲取由Request-URI所標識的資源的響應消息報頭
PUT 請求服務器存儲一個資源,並用Request-URI做爲其標識
DELETE 請求服務器刪除Request-URI所標識的資源
TRACE 請求服務器回送收到的請求信息,主要用於測試或診斷
CONNECT 保留未來使用
OPTIONS 請求查詢服務器的性能,或者查詢與資源相關的選項和需求socket
可是使用代理後,從代理服務上獲得的請求以下:函數
與第一張圖片對比一下,有什麼不一樣......第一行的資源路徑不對。當瀏覽器上設置代理請求時把整個url都做爲
資源路徑了,因此咱們要把http://www.cnblogs.com刪掉,而後代理服務器在把修改後的請求發送給目標
web服務器。就這麼簡單,固然CONNECT方法特別,要特別對待,因此先說其餘方法。
基本的思路:
一、代理服務器運行監聽,當有客戶端瀏覽器請求到來時經過accept()得到client句柄(或者叫描述符);
二、利用client描述符接收瀏覽器發來的request,分離出第一行爲了修改第一行和得到method,
要去掉的(如上圖http://www.cnblogs.com)的部分,除去http://的部分用targetHost表示吧。
三、經過第2步可以得到方法method、request和targetHost,這一步能夠根據不一樣的method作不一樣的處理,
因爲GET、POET、PUT、DELETE等除了CONNECT處理基本一致,因此處理首行,好比:
GET http://www.cnblogs.com/ HTTP/1.1 替換爲 GET / HTTP/1.1
此時targetHost也就是紅色的部分,默認的請求80端口,此時port爲80;若是targetHost中有端口(好比www.cnblogs.com:8081),
就要分理處端口,此時port爲8081。而後根據targetHost和port鏈接到目標服務器target了,實現代碼以下:
1 def getTargetInfo(self,host): #處理targetHost得到網址和端口,做爲返回值。 2 port=0 3 site=None 4 if ':' in host: 5 tmp=host.split(':') 6 site=tmp[0] 7 port=int(tmp[1]) 8 else: 9 site=host 10 port=80 11 return site,port 12 13 def commonMethod(self,request): #處理除CONNECT之外的方法 14 tmp=self.targetHost.split('/') 15 net=tmp[0]+'//'+tmp[2] 16 request=request.replace(net,'') #替換掉首行沒必要要的部分 17
18 targetAddr=self.getTargetInfo(tmp[2]) #調用上面的函數 19 try: 20 (fam,_,_,_,addr)=socket.getaddrinfo(targetAddr[0],targetAddr[1])[0] 21 except Exception as e: 22 print e 23 return 24 self.target=socket.socket(fam) 25 self.target.connect(addr) #鏈接到目標web服務
四、這一步就好辦了,根據第三步處理後的request就能夠self.target.send(request)發送給web服務器了。
五、這一步web服務器的reponse反響經過代理服務直接轉發給客戶端就好了,本人用了非阻塞select,能夠試試epoll。
基本步驟就是這樣,使用的方法函數能夠改進,好比主函數部分使用的多線程或者多進程,怎樣選擇......
可是思路差很少都是這樣啦。想測試的話,chrome安裝SwitchySharp插件,設置一下,代理端口8083;
firefox插件autoproxy。
對於connect的處理還在解決,因此如今這個代理程序不支持https協議(github已更新)。
代理服務能夠得到http協議的全部信息,想了解學習http,利用代理服務器是個不錯的方法。
下面附上代碼,更新會在https://github.com/915546302/tinyproxy
1 #-*- coding: UTF-8 -*- 2 import socket,select 3 import sys 4 import thread 5 from multiprocessing import Process 6 7 class Proxy: 8 def __init__(self,soc): 9 self.client,_=soc.accept() 10 self.target=None 11 self.request_url=None 12 self.BUFSIZE=4096 13 self.method=None 14 self.targetHost=None 15 def getClientRequest(self): 16 request=self.client.recv(self.BUFSIZE) 17 if not request: 18 return None 19 cn=request.find('\n') 20 firstLine=request[:cn] 21 print firstLine[:len(firstLine)-9] 22 line=firstLine.split() 23 self.method=line[0] 24 self.targetHost=line[1] 25 return request 26 def commonMethod(self,request): 27 tmp=self.targetHost.split('/') 28 net=tmp[0]+'//'+tmp[2] 29 request=request.replace(net,'') 30 targetAddr=self.getTargetInfo(tmp[2]) 31 try: 32 (fam,_,_,_,addr)=socket.getaddrinfo(targetAddr[0],targetAddr[1])[0] 33 except Exception as e: 34 print e 35 return 36 self.target=socket.socket(fam) 37 self.target.connect(addr) 38 self.target.send(request) 39 self.nonblocking() 40 def connectMethod(self,request): #對於CONNECT處理能夠添加在這裏 41 pass 42 def run(self): 43 request=self.getClientRequest() 44 if request: 45 if self.method in ['GET','POST','PUT',"DELETE",'HAVE']: 46 self.commonMethod(request) 47 elif self.method=='CONNECT': 48 self.connectMethod(request) 49 def nonblocking(self): 50 inputs=[self.client,self.target] 51 while True: 52 readable,writeable,errs=select.select(inputs,[],inputs,3) 53 if errs: 54 break 55 for soc in readable: 56 data=soc.recv(self.BUFSIZE) 57 if data: 58 if soc is self.client: 59 self.target.send(data) 60 elif soc is self.target: 61 self.client.send(data) 62 else: 63 break 64 self.client.close() 65 self.target.close() 66 def getTargetInfo(self,host): 67 port=0 68 site=None 69 if ':' in host: 70 tmp=host.split(':') 71 site=tmp[0] 72 port=int(tmp[1]) 73 else: 74 site=host 75 port=80 76 return site,port 77 78 if __name__=='__main__': 79 host = '127.0.0.1' 80 port = 8083 81 backlog = 5 82 server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 83 server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 84 server.bind((host,port)) 85 server.listen(backlog) 86 while True: 87 thread.start_new_thread(Proxy(server).run,()) 88 # p=Process(target=Proxy(server).run, args=()) #多進程 89 # p.start() 90 91