如何使用 Python 流套接字作为代理?

发布于 2024-12-11 20:38:56 字数 975 浏览 0 评论 0原文

我正在尝试编写一个侦听端口的 python 进程,当客户端连接到它时,它会启动一个执行以下操作的线程:

  1. 连接到远程服务 (http://193.108.24.18:8000 /magicFM)

  2. 将收到的任何数据传递到连接的客户端(恰好是 Windows Media Player)

故事是,我想在工作时收听广播,但我不能,因为我在另一个国家/地区(仅在全国范围内可用),我无法更改计算机上的代理设置...... 但我有这台服务器,我想将其用作代理。

提前致谢。

这是我到目前为止所做的:

#!/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')

I'm trying to write a python process which listens on a port and when a client connects to it, it starts up a thread which does the following:

  1. Connects to a remote service (http://193.108.24.18:8000/magicFM)

  2. Passes any data received to the connected client (which happens to be Windows Media Player)

The story is that I want to listen to my radio at work, but I cannot because I am in another country(available only nationally) and I cannot change the proxy settings on my computer....
But I have this server which I would like to use as a proxy.

Thanks in advance.

Here's what I did so far:

#!/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()

but it fails... with:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

清旖 2024-12-18 20:38:56

首先,要使用 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 多路复用数据流。

我现在没有时间,这段代码更多的是它的外观草图。您需要适当的错误处理,并且可能需要在此函数中提供不错的错误消息,但如果没有人提出完整的解决方案,我可能会努力完成此代码。

As a start, to connect to a remote site using TCP, use this code

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

You now have an open socket connected to a remote server. You would have to create a listen socket and wait on this one for a connection. As soon as the connection is there, multiplex data streams using select.

I don't have the time right now, this code is more of a sketch how it might look like. You would need proper error handling and maybe nice error messages in this function, but if noone comes up with a complete solution I may make the effort to complete this code.

梦行七里 2024-12-18 20:38:56

我只能猜测,但也许你的问题出在客户端。

我不知道您的客户端尝试建立哪些连接,但也许预期的内容和实际传输的内容之间存在冲突:

  • 您从客户端获得连接,该连接可能会发送一些请求数据。
  • 如果这些与您使用 urllib2.urlopen() 发送到流的数据不匹配,或者来自那里的答案不匹配,则客户端会取消连接,从而导致套接字损坏。

我看到两种解决方案:

尝试

  • 将应答行(HTTP/xx 200 OK 等)和标头也发送回您的客户端 - 它应该位于 req.headers< 中的某个位置/代码> 左右。

或者

  • 根本不执行 urllib2.urlopen() ,而只是打开到那里的常规套接字连接。但是,您可能必须篡改请求的标头 - Host: 标头可能必须被替换。

I can only guess, but maybe your problem lives on the client side.

I don't know which connections your client tries to establish, but maybe there is a clash between what is expected and what is really transmitted:

  • You get a connection from your client, which probably sends some request data.
  • If these don't match with the data you send to the stream with urllib2.urlopen(), or the answer from there doesn't match, the client cancels the connection, letting you have a broken socket.

I see two solutions:

Either

  • Try to send the answer line (HTTP/x.x 200 OK or such) and the headers back to your client as well - it should be somewhere in req.headers or so.

Or

  • don't do a urllib2.urlopen() at all, but just open a regular socket connection to there. But then you'll probably have to tamper with the headers of the request - the Host: header will probably have to be replaced.
兰花执着 2024-12-18 20:38:56

扩展 glglgl 答案:你的问题在于破坏协议。

HTTP 协议规定:

  • 用户请求 GET /magicFM ...
  • 服务器响应元数据 200 OK ...
  • 服务器持续响应实际数据

查看更多详情:http://en.wikipedia.org/wiki/HTTP

urllib2.urlopen 隐藏所有内容不过,您的这种复杂性使它看起来像读取文件一样简单您的客户端希望代理的行为与普通的 http 服务器一样。这里的 urlopen 对你来说是一个错误的抽象。最好的策略是打开到服务器的套接字并启动两个并行循环:

  • 从客户端读取,写入服务器
  • 从服务器读取,写入客户端

(或在一个循环中进行非阻塞读取;或进行异步)

可能有一个复杂性:http协议指定“Host”标头,您的客户端将在请求中将其以代理地址作为值发送给代理,具体取决于您的无线电服务器的行为,您可能需要将客户端请求中的“Host:...”重写为正确的地址(尽管在现代互联网中通常是没关系)。

您会注意到的另一个有趣的副作用是:代理不会包含有关要打开的特定 URL 的任何信息,因为您的媒体客户端将为您提供它们。

To extend glglgl answer: your problem is in breaking protocol.

HTTP protocol specifies:

  • user request GET /magicFM ...
  • server response with metadata 200 OK ...
  • server continued response with actual data

See more for details: http://en.wikipedia.org/wiki/HTTP

urllib2.urlopen hides all this complication from you making it look as simple as reading file, though your client expect proxy to behave as normal http-server. Here urlopen is a wrong abstraction for you. Best strategy would be to open socket to server and start two parallel loops:

  • reading from client, writing to server
  • reading from server, writing to client

(or do it in one loop with non-blocking reads; or do asyncio)

There might be a complication: http-protocol specifies "Host" header which your client will send in request to proxy with proxy address as a value, depending on behavior of your radio-server you might need to rewrite "Host: ..." in client request to correct address (though in modern Internet usually it doesn't matter).

Also interesting side effect you will notice would be: proxy would not contain any information about specific URLs to open, as your media client will provide them for you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文