来自 编程应用 2019-09-04 19:49 的文章
当前位置: 六合联盟网 > 编程应用 > 正文

Python设计格局之代理情势实例,python达成代理服

翻墙常用的方式就是使用代理(Proxy),其基本过程如下:

代理服务原理很简单,就拿浏览器与web服务器来说。无非是A浏览器
发request给B代理,B代理再把request把送给C web服务,然后C的reponse->B->A。
要写web代理服务就要先了解下http协议,当然并不要多深入,除非要实现强大的功能:修改XX信息、
负载均衡等。http请求由三部分组成:请求行、消息报头、请求正文;
详细的网上有,想了解可以看看。下面是一个正常的GET请求头(Cookie部分本人没截屏,使用的系统w7):

浏览器<-->代理服务器<-->服务器

图片 1

如果浏览器请求不到服务器,或者服务器无法响应浏览器,我们可以设定将浏览器的请求传递给代理服务器,代理服务器将请求转发给服务器。然后,代理服务器将服务器的响应内容传递给浏览器。当然,代理服务器在得到请求或者响应内容的时候,本身也可以做些处理,例如缓存静态内容以加速,或者说提取请求内容或者响应内容做些正当或者不正当的分析。这种翻墙方式,就是设计模式中代理模式(Proxy Pattern)的一个具体例子。

可以看到首行:GET是请求方法, /是路径,在后面是协议版本;第二行以后是请求报头,都是键值对形式;
GET方法没有正文。post有正文,除此之外,请求方法头部基本一致,每一行结尾都是rn。
基本的请求方法,如下:

维基百科对代理模式做了以下解释:

GET        请求获取Request-URI所标识的资源
POST      在Request-URI所标识的资源后附加新的数据
HEAD      请求获取由Request-URI所标识的资源的响应消息报头
PUT         请求服务器存储一个资源,并用Request-URI作为其标识
DELETE   请求服务器删除Request-URI所标识的资源
TRACE     请求服务器回送收到的请求信息,主要用于测试或诊断
CONNECT 保留将来使用
OPTIONS  请求查询服务器的性能,或者查询与资源相关的选项和需求
但是使用代理后,从代理服务上得到的请求如下:
图片 2

复制代码 代码如下:

与第一张图片对比一下,有什么不同......第一行的资源路径不对。当浏览器上设置代理请求时把整个url都作为资源路径了,所以我们要把域名删掉,然后代理服务器在把修改后的请求发送给目标
web服务器。就这么简单,当然CONNECT方法特别,要特别对待,所以先说其他方法。
基本的思路:
1、代理服务器运行监听,当有客户端浏览器请求到来时通过accept()获得client句柄(或者叫描述符);
2、利用client描述符接收浏览器发来的request,分离出第一行为了修改第一行和获得method,
要去掉的的部分,除去
3、通过第2步能够获得方法method、request和targetHost,这一步可以根据不同的method做不同的处理,
由于GET、POET、PUT、DELETE等除了CONNECT处理基本一致,所以处理首行,比如:

In computer programming, the proxy pattern is a software design pattern. A proxy, in its most general form, is a class functioning as an interface to something else. The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate.

复制代码 代码如下:

基于面向过程实现的代理模式

GET HTTP/1.1
替换为
GET / HTTP/1.1

下面是一段体现该设计模式中心的面向过程的python代码:

此时targetHost也就是红色的部分,默认的请求80端口,此时port为80;如果targetHost中有端口(比如www.a.com:8081),
就要分理处端口,此时port为8081。然后根据targetHost和port连接到目标服务器target了,实现代码如下:

复制代码 代码如下:

复制代码 代码如下:

def hello():
    print 'hi, i am hello'

def getTargetInfo(self,host): #处理targetHost获得网址和端口,作为返回值。
        port=0
        site=None
        if ':' in host:
            tmp=host.split(':')
            site=tmp[0]
            port=int(tmp[1])
        else:
            site=host
            port=80
        return site,port
def commonMethod(self,request): #处理除CONNECT以外的方法
        tmp=self.targetHost.split('/')
        net=tmp[0]+'//'+tmp[2]
        request=request.replace(net,'') #替换掉首行不必要的部分
        targetAddr=self.getTargetInfo(tmp[2]) #调用上面的函数
        try:
            (fam,_,_,_,addr)=socket.getaddrinfo(targetAddr[0],targetAddr[1])[0]
        except Exception as e:
            print e
            return
        self.target=socket.socket(fam)
        self.target.connect(addr) #连接到目标web服务

def proxy():
    print 'prepare....'
    hello()
    print 'finish....'

4、这一步就好办了,根据第三步处理后的request就可以self.target.send(request)发送给web服务器了。
5、这一步web服务器的reponse反响通过代理服务直接转发给客户端就行了,本人用了非阻塞select,可以试试epoll。
基本步骤就是这样,使用的方法函数可以改进,比如主函数部分使用的多线程或者多进程,怎样选择......
但是思路差不多都是这样啦。想测试的话,chrome安装SwitchySharp插件,设置一下,代理端口8083;
firefox插件autoproxy。
 对于connect的处理还在解决中(如果有博友帮助就更好了),所以现在这个代理程序不支持https协议。
代理服务可以获得http协议的所有信息,想了解学习http,利用代理服务器是个不错的方法。
下面附上代码

if __name__ == '__main__':
    proxy()

复制代码 代码如下:

运行结果:

#-*- coding: UTF-8 -*-
import socket,select
import sys
import thread
from multiprocessing import Process
class Proxy:
    def __init__(self,soc):
        self.client,_=soc.accept()
        self.target=None
        self.request_url=None
        self.BUFSIZE=4096
        self.method=None
        self.targetHost=None
    def getClientRequest(self):
        request=self.client.recv(self.BUFSIZE)
        if not request:
            return None
        cn=request.find('n')
        firstLine=request[:cn]
        print firstLine[:len(firstLine)-9]
        line=firstLine.split()
        self.method=line[0]
        self.targetHost=line[1]
        return request
    def commonMethod(self,request):
        tmp=self.targetHost.split('/')
        net=tmp[0]+'//'+tmp[2]
        request=request.replace(net,'')
        targetAddr=self.getTargetInfo(tmp[2])
        try:
            (fam,_,_,_,addr)=socket.getaddrinfo(targetAddr[0],targetAddr[1])[0]
        except Exception as e:
            print e
            return
        self.target=socket.socket(fam)
        self.target.connect(addr)
        self.target.send(request)
        self.nonblocking()
    def connectMethod(self,request): #对于CONNECT处理可以添加在这里
        pass
    def run(self):
        request=self.getClientRequest()
        if request:
            if self.method in ['GET','POST','PUT',"DELETE",'HAVE']:
                self.commonMethod(request)
            elif self.method=='CONNECT':
                self.connectMethod(request)
    def nonblocking(self):
        inputs=[self.client,self.target]
        while True:
            readable,writeable,errs=select.select(inputs,[],inputs,3)
            if errs:
                break
            for soc in readable:
                data=soc.recv(self.BUFSIZE)
                if data:
                    if soc is self.client:
                        self.target.send(data)
                    elif soc is self.target:
                        self.client.send(data)
                else:
                    break
        self.client.close()
        self.target.close()
    def getTargetInfo(self,host):
        port=0
        site=None
        if ':' in host:
            tmp=host.split(':')
            site=tmp[0]
            port=int(tmp[1])
        else:
            site=host
            port=80
        return site,port
if __name__=='__main__':     
    host = '127.0.0.1'
    port = 8083
    backlog = 5
    server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    server.bind((host,port))
    server.listen(5)
    while True:
        thread.start_new_thread(Proxy(server).run,())
        # p=Process(target=Proxy(server).run, args=()) #多进程
        # p.start()

复制代码 代码如下:

发request给B代理,B代理再把request把送给C web服务,然后C的reponse-B-A。 要...

prepare....
hi, i am hello
finish....

有没有想到装饰器?

基于面向对象实现的代理模式

复制代码 代码如下:

class AbstractSubject(object):

    def __init__(self):
        pass

    def request(self):
        pass

class RealSubject(AbstractSubject):

    def __init__(self):
        pass
    def request(self):
        print 'hi, i am RealSubject'

class ProxySubject(AbstractSubject):

    def __init__(self):
        self.__rs = RealSubject()

    def request(self):
        self.__beforeRequest()
        self.__rs.request()
        self.__afterRequest()

    def __beforeRequest(self):
        print 'prepare....'

    def __afterRequest(self):
        print 'finish....'

if __name__ == '__main__':
    subject = ProxySubject()
    subject.request()

如果RealSubject的初始化函数init有参数,代理类ProxySubject可以作两种方式的修改: 第一种: ProxySubject的init方法同样也有参数,初始化代理类的时候将初始化参数传递给RealSubject。 第二种: 将ProxySubject的init方法改为:

复制代码 代码如下:

def __init__(self):
    self.__rs = None

将ProxySubject的request方法改为:

复制代码 代码如下:

def request(self, *args, **kwargs):
    if self.__rs == None:
        self.__rs = RealSubject(*args, **kwargs)
    self.__beforeRequest()
    self.__rs.request()
    self.__afterRequest()

的类似形式。

浏览器--代理服务器--服务器 如果浏览器请求不到服务器,或者服务器无法响应...

本文由六合联盟网发布于编程应用,转载请注明出处:Python设计格局之代理情势实例,python达成代理服

关键词: