Как я могу использовать потоковый сокет Python в качестве прокси? - PullRequest
1 голос
/ 24 октября 2011

Я пытаюсь написать процесс Python, который прослушивает порт, и когда клиент подключается к нему, он запускает поток, который выполняет следующее:

  1. Подключение к удаленной службе (http://193.108.24.18:8000/magicFM)

  2. Передает любые данные, полученные подключенному клиенту (который является проигрывателем Windows Media)

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

Заранее спасибо.

Вот что я сделал до сих пор:

#!/usr/bin/env python
import socket, urllib2

TCP_IP = '0.0.0.0'
TCP_PORT = 5566
BUFFER_SIZE = 16 * 1024  #16 kb/s
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)
conn, addr = s.accept()
print 'Connection address:', addr
req = urllib2.urlopen('http://193.108.24.18:8000/magicFM')

while 1:
    chunk = req.read(BUFFER_SIZE)
    if not chunk: break
    conn.send(chunk)


conn.close()

но не получается ... с:

Traceback (most recent call last):
  File "./magicfmproxy.py", line 17, in ?
    conn.send(chunk)
socket.error: (32, 'Broken pipe')

Ответы [ 3 ]

2 голосов
/ 24 октября 2011

Для начала, чтобы подключиться к удаленному сайту через TCP, используйте этот код

import socket, struct

def connectToHost(host, port=80, timeout=0):
    try:
            sock=socket.socket()
            timeval=struct.pack("2I", timeout, 0)
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, timeval)
            sock.connect((host, port))
            return sock
    except:
            return None

Теперь у вас есть открытый сокет, подключенный к удаленному серверу. Вы должны создать сокет прослушивания и подождать на этом соединении. Как только соединение установлено, мультиплексируйте потоки данных, используя select.

Сейчас у меня нет времени, этот код - скорее набросок, как он может выглядеть. Вам потребуется правильная обработка ошибок и, возможно, хорошие сообщения об ошибках в этой функции, но если никто не придет к полному решению, я могу приложить усилия для завершения этого кода.

1 голос
/ 24 октября 2011

Я могу только догадываться, но, возможно, ваша проблема живет на стороне клиента.

Я не знаю, какие соединения пытается установить ваш клиент, но, возможно, существует конфликт между тем, что ожидается, и тем, что действительно передается:

  • Вы получаете соединение от своего клиента, который, вероятно, отправляет некоторые данные запроса.
  • Если они не совпадают с данными, которые вы отправляете в поток с urllib2.urlopen(), или ответ оттуда не совпадает, клиент отменяет соединение, давая вам сломанный сокет.

Я вижу два решения:

Или

  • Попробуйте отправить строку ответа (HTTP/x.x 200 OK или около того) и заголовки обратно вашему клиенту - он должен быть где-то в req.headers или около того.

или

  • вообще не делайте urllib2.urlopen(), а просто откройте для этого обычное сокетное соединение. Но тогда вам, вероятно, придется вмешиваться в заголовки запроса - заголовок Host:, вероятно, придется заменить.
0 голосов
/ 25 октября 2011

Чтобы расширить ответ glglgl: ваша проблема в нарушении протокола.

Протокол HTTP указывает:

  • запрос пользователя GET /magicFM ...
  • ответ сервера с метаданными 200 OK ...
  • сервер продолжил ответ с фактическими данными

Подробнее см .: http://en.wikipedia.org/wiki/HTTP

urllib2.urlopen скрывает все эти сложности от вас, делая его таким же простым, как чтение файла, хотя ваш клиент ожидает, что прокси будет вести себя как обычный http-сервер. Здесь urlopen - неправильная абстракция для вас. Лучшая стратегия - открыть сокет для сервера и запустить два параллельных цикла:

  • чтение с клиента, запись на сервер
  • чтение с сервера, запись на клиент

(или сделать это за один цикл с неблокирующим чтением; или сделать asyncio)

Это может быть осложнено: http-протокол указывает заголовок «Host», который ваш клиент отправит в запросе на прокси-сервер с прокси-адресом в качестве значения, в зависимости от поведения вашего радиосервера, вам может потребоваться переписать «Host:. .. »в запросе клиента на корректный адрес (хотя в современном Интернете это обычно не имеет значения).

Также интересным побочным эффектом, который вы заметите, будет то, что прокси-сервер не будет содержать никакой информации об определенных URL-адресах для открытия, поскольку ваш медиа-клиент предоставит их вам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...