[Python]pythonProxy 实现代理服务器详解

python代理服务器的实现

  1. Python IDE
    网络上有很多关于python IDE的选择,个人比较偏向Anaconda,里面的界面比较友好。缺点是库太大,大概有380M,下载时间太长,在测试中的网络环境太恶劣,希望能用apt-get install来安装。最后发现了这个IDLE比较方便,安装如下:
sudo apt-get install idle-python2.7
//上面可以不输入2.7然后使用tab联想,可以看到还有3.4的版本,这里使用了2.7的版本
  1. 具体的代理实现。
    参考文献
    上面的参考文献是代理的具体实现,主要实现了A–>B—>C—->Internet中的B的代码,B通过C的代理上网,然后通过这段代码给A开代理,这里仅对这段代码进行分析。

2.1 创建日志即日志目录,判断是否有logs这个目录,无则进行创建。然后对这个logging的基本信息进行配置。

logsDir = "logs"
if not os.path.isdir(logsDir);
    os.mkdir(logsDir)
logging.basicConfig(level=logging.DEBUG,
 format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                    datefmt='%a,%d %b %Y %H:%M:%S ',
                    filename='logs/logs.log',
                    filemode='a')

2.2 设置代理的ip和限制最大代理的数目:

to_addr=('10.13.20.3',3128)
maxConnections = 32

2.3 将所有的代理操作封装成类
2.3.1 首先写这个初始化函数,传入addr,这个是在调用的时候创建。

class Proxy:
        def __init__(self,addr):
            self.proxy = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
            self.proxy.bind(addr)
            self.proxy.listen(maxConnections)
            self.inputs = {self.proxy:None}
            self.route = {}

2.3.1.1 self指的是类实例对象本身,基础知识见self解释

这个类有成员变量 proxy/inputs/route。
2.3.1.2 其中proxy是socket对象,这个socket是流对象,基础知识见python中的socket
将这个proxy对象绑定到指定的地址,并设置最大的监听数目。
2.3.1.3 inputs成员是以proxy为key的一个集合,用来记录从A过来的socket(A–>B—>C—->Internet)
2.3.1.4 route和inputs相反,记录返程socket(A<–B<—C<—-Internet)

2.3.2 在上面的Proxy类中定义这个主要的方法类serve_forever(self)

def serve_forever(self):
            logging.info('proxy listen....')
            while 1:
                readable,_,_=select.select(list(self.inputs.keys()),[],[])
                for self.sock in readable:
                    if self.sock == self.proxy:
                        self.on_join()
                    else:
                        try:
                            data = self.sock.recv(8192)
                        except Exception, e:
                            logging.error(str(e))
                            self.on_quit()
                            continue

                        if not data:
                            self.on_quit()
                        else:
                            try:
                                self.route[self.sock].send(data)
                            except Exception,e:
                                logging.error(str(e))
                                self.on_quit()
                                continue

2.3.2.1 这里使用了select模块,基础知识见select解释
这个select这里是为了承担起服务器和多个客户端通信的作用。select的原型为(rlist,wlist,xlist[,timeout]),rlist是等待读取的对象,wlist是等待写入的对象,xlist是等待异常的对象,最后一个是可选对象,指定等待的时间,单位是s,将要返回对象三元组,所以这里只接收等待读取的对象。
2.3.2.2 如果这个key是proxy对象,执行这个Proxy类的on_join()方法,这个方法具体实现见2.3.3,主要记录从C到A的返程socket(A<–B<—C<—-Internet)
2.3.2.3 如果不是sock.proxy成员,这里主要是看addr是否一致。不一致需要接收数据。接收完成数据需要进行判断if not data,即是否为空,如果不为空,则对这个self.sock进行发送数据。这里使用了self.route[self.sock]来确定要送达目的的socket,例如从A到B收到了报文,这个时候self.route[self.sock]确定了要发往C的socket。如果收到从C回应的报文,则反向发的报文为 self.route[forward] = client。
2.3.2.4 所有有异常的情况下需要执行on_quit()操作,具体见2.3.4。

2.3.3 on_join(self)方法。

def on_join(self):
            client,addr = self.proxy.accept()
            logging.info("proxy client "+str(addr)+' connect')
            forward = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
            try:
                forward.connect(to_addr)
            except Exception, e:
                logging.error(str(e))
                client.close()
                return
            self.inputs[client] = None
            self.inputs[forward] = None

            self.route[client] = forward
            self.route[forward] = client

2.3.3.1 这里proxy类是socket对象,调用它的accept方法,具体使用见上面python中socket的解释。accept方法返回一个含有两个元素的 元组(connection,address)。第一个元素connection是新的socket对象,服务器必须通过它与客户通信;第二个元素 address是客户的Internet地址。
2.3.3.2 定义这个forward变量,是一个新的socket对象。并使用connect链接到C设备(A–>B–>C–>internet)。
2.3.3.3 然后在这个route中记录这个返程的报文应该调用的链接。

2.3.4 on_quit()方法,主要是对各个socket进行关闭。

def on_quit(self):
            ls = [self.sock]
            if self.sock in self.route:
                ls.append(self.route[self.sock])
            for s in ls:
                if s in self.inputs:
                    del self.inputs[s]
                if s in self.route:
                    del self.route[s]
                s.close()
  1. 最后在代码中使用下面这段代码初始话Proxy类,使用了8192为代理端口,然后其他设备例如A就可以使用这个B设备的IP和端口号进行代理上网。
if __name__ == "__main__":
    try:
        Proxy(('',8192)).serve_forever()
    except KeyboardInterrupt, e:
        logging.error("KeyboardInterrupt"+str(e))

猜你喜欢

转载自blog.csdn.net/xsjyahoo/article/details/51568712