|
|||||||
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() Данный метод закрывает соединение между прокси-сервером и целевым сервером, а также между прокси-сервером и клиентом. Соответствующие объекты удаляются. Заключение Перед вами полноценный прокси-сервер. Как видите, компактное приложение решает все поставленные задачи. Источник Перевод Станислава Петренко |
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|