MyTetra Share
Делитесь знаниями!
100 строк кода: Прокси-сервер на Python
Время создания: 17.07.2018 16:49
Текстовые метки: python proxy server example
Раздел: Python
Запись: Velonski/mytetra-database/master/base/153182817247mumkd4cp/text.html на raw.githubusercontent.com

Что такое прокси-сервер?

Прокси-сервер (proxy server) – это сервер, исполняющий роль посредника между клиентом и целевым сервером. Прокси-сервер действует «от лица» клиента и, в зависимости от поставленной задачи, может выполнять различные преобразования данных. На рисунке ниже показана логика работы прокси-сервера:




Мы поставили перед собой задачу разработать прокси-сервер, используя только стандартные библиотеки Python. Перед началом разработки были сформулированы следующие критерии функциональности приложения:


Каждое новое соединение клиента с прокси-сервером, должно инициировать новое соединение с целевым сервером.

Каждый пакет данных, приходящий на прокси-сервер от клиента, должен пересылаться целевому серверу.

Каждый пакет данных, приходящий на прокси-сервер от целевого сервера, должен пересылаться соответствующему клиенту.

Поддержка работы с несколькими клиентами.

Высокая скорость.

Малый объем потребляемых ресурсов.

Ниже представлен код готового приложения:


#!/usr/bin/python


# This is a simple port-forward / proxy, written using only the default python


# library. If you want to make a suggestion or fix something you can contact-me


# at voorloop_at_gmail.com


# Distributed over IDC(I Don't Care) license


import socket


import select


import time


import sys




# Changing the buffer_size and delay, you can improve the speed and bandwidth.


# But when buffer get to high or delay go too down, you can broke things


buffer_size = 4096


delay = 0.0001


forward_to = ('smtp.zaz.ufsk.br', 25)




class Forward:


def __init__(self):


self.forward = socket.socket(socket.AF_INET, socket.SOCK_STREAM)




def start(self, host, port):


try:


self.forward.connect((host, port))


return self.forward


except Exception, e:


print e


return False




class TheServer:


input_list = []


channel = {}




def __init__(self, host, port):


self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)


self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)


self.server.bind((host, port))


self.server.listen(200)




def main_loop(self):


self.input_list.append(self.server)


while 1:


time.sleep(delay)


ss = select.select


inputready, outputready, exceptready = ss(self.input_list, [], [])


for self.s in inputready:


if self.s == self.server:


self.on_accept()


break




self.data = self.s.recv(buffer_size)


if len(self.data) == 0:


self.on_close()


break


else:


self.on_recv()




def on_accept(self):


forward = Forward().start(forward_to[0], forward_to[1])


clientsock, clientaddr = self.server.accept()


if forward:


print clientaddr, "has connected"


self.input_list.append(clientsock)


self.input_list.append(forward)


self.channel[clientsock] = forward


self.channel[forward] = clientsock


else:


print "Can't establish connection with remote server.",


print "Closing connection with client side", clientaddr


clientsock.close()




def on_close(self):


print self.s.getpeername(), "has disconnected"


#remove objects from input_list


self.input_list.remove(self.s)


self.input_list.remove(self.channel[self.s])


out = self.channel[self.s]


# close the connection with client


self.channel[out].close() # equivalent to do self.s.close()


# close the connection with remote server


self.channel[self.s].close()


# delete both objects from channel dict


del self.channel[out]


del self.channel[self.s]




def on_recv(self):


data = self.data


# here we can parse and/or modify the data before send forward


print data


self.channel[self.s].send(data)




if __name__ == '__main__':


server = TheServer('', 9090)


try:


server.main_loop()


except KeyboardInterrupt:


print "Ctrl C - Stopping server"


sys.exit(1)

Пояснение

Класс Forward

Устанавливает соединение между прокси-сервером и целевым сервером.


Класс TheServer

Основной класс приложения.


Метод TheServer.main_loop()

Список input_list содержит все доступные сокеты, управление которыми осуществляется с помощью select.select(). Первым в список добавляется серверный сокет самого прокси-сервера. Каждое новое подключение к этому сокету будет инициировать вызов метода on_accept().


Если текущий сокет из списка inputready, возвращенного select.select(), не предполагает новое соединение, значит, мы имеем дело с входящими данными (возможно от целевого сервера, возможно от клиента). Если размер данных равен нулю, значит, это запрос на закрытие соединения, в противном случае пакет необходимо переслать в соответствующее место назначения.


Метод TheServer.on_accept()

Этот метод устанавливает новое соединение с целевым сервером, а также принимает подключение текущего клиента. Оба сокета добавляются в список input_list для дальнейшей обработки в методе main_loop(). Оба сокета также сохраняются в словаре channel для сопоставления конечных точек назначения (клиент <=> целевой сервер).


Метод TheServer.on_recv()

Этот метод выполняет обработку данных (при необходимости) и пересылает их по месту назначения (клиент <= прокси-сервер => целевой сервер).


Метод TheServer.on_close()

Данный метод закрывает соединение между прокси-сервером и целевым сервером, а также между прокси-сервером и клиентом. Соответствующие объекты удаляются.


Заключение

Перед вами полноценный прокси-сервер. Как видите, компактное приложение решает все поставленные задачи.


Источник


Перевод Станислава Петренко

Так же в этом разделе:
 
MyTetra Share v.0.59
Яндекс индекс цитирования