如何正确中继TCP流量在套接字之间?

4 投票
3 回答
3775 浏览
提问于 2025-04-15 21:24

我正在尝试写一些Python代码,目的是在两个TCP套接字之间建立一个看不见的中继。现在我的方法是设置两个线程,每个线程一次读取并写入1KB的数据,方向是固定的(也就是说,一个线程负责从A到B,另一个线程负责从B到A)。

这种方法在某些应用和协议中是有效的,但并不是万无一失的——有时某些应用在通过这个基于Python的中继运行时会表现得不一样,甚至会崩溃。

我认为这是因为当我在套接字A上完成读取时,那里运行的程序认为它的数据已经到达B了,实际上我这个“狡猾的中间人”还没有把数据发送给B。如果B还没准备好接收数据(这时send()会阻塞一段时间),那么就会出现这样一种情况:A认为它已经成功地将数据发送给B,但我却还在手里拿着数据,等着send()调用执行。我觉得这就是我在某些应用中发现的行为差异的原因。我是不是漏掉了什么,还是说我的理解是正确的?

如果是这样,我真正想问的是:有没有办法解决这个问题?是否可以在确认B准备好接收数据时再从套接字A读取?或者有没有其他技术可以让我在[已经打开和建立的] TCP套接字之间建立一个真正“看不见”的双向中继?

3 个回答

1

我觉得这不太可能是你的问题。

一般来说,发送数据的应用程序无法知道接收数据的应用程序什么时候真正调用recv()来读取数据:发送方的send()可能已经完成,但在发送和接收的操作系统中,TCP协议会进行缓冲、流量控制、重传等操作。

即使没有你中间的中继,A想要“认为它的数据已经到达B”的唯一方法,就是收到B的回复,告诉它“没错,我收到了”。

1

也许你正在代理的应用程序写得不好。

比如说,如果我调用 recv(fd, buf, 4096, 0);,并不能保证我会收到4096个字节。系统会尽力提供这些数据。

如果1k不是你应用程序的 recvsend 大小的倍数,而这个应用程序又有问题,那么把发送的数据分成1k的块就会导致应用程序出错。

5

我们能不能在知道B已经准备好接收数据的时候,只从A这个socket读取数据呢?

当然可以:你可以在A和B这两个socket上使用 select.select(如果返回结果显示只有一个socket准备好了,那就对另一个socket再使用一次),只有在确认它们都准备好的时候,才从A读取数据并写入B。例如:

import select

def fromAtoB(A, B):
    r, w = select.select([A], [B], [])
    if not r: select.select([A], [], [])
    elif not w: select.select([], [B], [])
    B.sendall(A.recv(4096))

撰写回答