【问题标题】:Is it possible to send and receive packets through different sockets?是否可以通过不同的套接字发送和接收数据包?
【发布时间】:2013-12-15 01:45:06
【问题描述】:

我在 Google 上搜索了一段时间,并在此处阅读了有关通过套接字发送和接收的相关问题,但找不到我的问题的答案:

我可以通过不同的套接字发送和接收数据包吗?

我想实现这样的东西:

HOST='127.0.0.1'
PORT=10000
recvSock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW)
sendSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

sendSock.sendto("",(HOST,PORT))
response,addr = recvSock.recvfrom(65535)

基本上,我想通过作为 UDP 套接字的 sendSock 向回显服务器发送消息,并通过作为原始套接字的 recvSock 从服务器接收应答。

有可能吗?如果是,那么如何?关于代码不起作用。

【问题讨论】:

  • 可以,但您需要对 UDP 协议进行解码/编码
  • @Torxed:“确定”? recvSock 不可能收到作为对他的sendSock.sendto 的响应而发送的数据。他必须做一些完全不同的事情。
  • 同时,关于你的最后一个问题,我说如果你想让任何人帮助解决这样的问题,你必须提供你的平台和版本。今天仍然如此。对此类非特定于平台的任何问题的任何答案都将是有限的。任何人都可以说“有些平台可以做 X,但他们以不同的方式做,而其他平台不能做 X,但可以做 Y,这可能是也可能不是你需要的,还有一些平台不能做类似的事情,而且这些年来大部分都变了”,这对你写你想写的代码没有帮助。

标签: python sockets udp client-server raw-sockets


【解决方案1】:

让我们后退一点,因为你写的问题没有多大意义,但我想我知道你想做什么。

当您创建一个 UDP 套接字并在其上发送消息时,它做了两件事:首先,它选择了一个适当的源地址,以及来自临时范围的任意端口,并将您的套接字绑定到该地址。 (UDP 地址是主机 IP 和端口。)然后它从源地址向目标地址发送消息。

当远程端响应时,它将向该源地址发送一条消息。内核收到该消息并看到它针对的是您的 UDP 套接字绑定到的地址。因此它将消息传递到该套接字。

这意味着recvfrom 只能在该套接字上工作,而不能在另一个套接字上工作。仅在未绑定到任何东西的不同套接字上调用 recvfrom 不会有任何好处。

如果您可以将两个套接字绑定到同一个地址会怎样?好吧,您可以使用套接字选项SO_REUSEADDR 和/或SO_REUSEPORT。有关每个选项何时允许的一些(高度特定于平台的)详细信息,请参阅this question。但是,即使它被允许,当接收到一个有两个套接字绑定到它的地址的消息时会发生什么?好吧,这也是高度特定于平台的。理论上,消息是任意传递到其中一个套接字的。在实践中,在某些平台上,内核会记住谁最近发送到响应的源地址,或与源地址在同一网络上的任何地址,或最近绑定的人,或其他规则,并传递给那个一。因此,根据您的平台,这可能对您有帮助,也可能无济于事——您可以使用bind 一个套接字,将其用于sendto,然后将bind 用于第二个套接字,然后将recvfrom 用于第二个套接字并获得响应。或者它可能不会。如果它没有做你想做的事,不要试图与你的内核抗争;如果你想躲起来,还有更好的方法。

如果绑定到更具包容性的地址会怎样?例如,您可以将第一个套接字绑定到('127.0.0.1', 12345),然后将第二个套接字绑定到('0.0.0.0', 12345),它们是不同的地址,对吧?好吧,这基本上解释为将它们都绑定到相同的套接字 - 特定于平台的差异在我上面链接的相同答案中进行了解释,如果你被允许这样做,行为将与你相同使用相同的地址。在大多数平台上,即使您将(原始)套接字绑定到接口而不是地址也是如此。

除此之外,经典的 BSD 原始套接字甚至无法接收 UDP(或 TCP)数据包。除非您构建一个数据包过滤器来重新路由它们,否则它们总是由内核处理并传递到 UDP 或 TCP 套接字(或防火墙,如果没有)。 Raw IP Networking FAQ 准确解释了根据最初定义的 BS​​D 原始套接字协议您可以接收和不能接收的内容。幸运的是,大多数现代平台都超越了原始的 BSD 套接字协议——不幸的是,它们都以不同的方式这样做。

您可以通过将套接字置于混杂模式来解决这两个问题,这意味着它将在路由之前获取其接口上的所有数据包。 (在每个平台上执行此操作的方法也不同。)然后由您决定哪些将被路由到您的 UDP 套接字并适当地处理它们。 (这对于 UDP 并不太难;对于 TCP,它变得更加复杂,因为 TCP 数据包在传送到套接字之前会被存储并按顺序放回。)

更简单的解决方案是使用您平台的数据包过滤器接口。

或者,更简单的是,使用 libpcap 之类的东西,它具有用于数据包捕获的跨平台包装器。

或者,更简单的是,使用scapy 之类的东西,它具有围绕libpcap 和其他一切必要的Python 包装器,或者wireshark,一个可以从Python 自动化的单独程序。采用这种方式可以轻松准确地捕获将传递到您的套接字的数据包并解析标头以及您想做的所有其他事情。

或者,某些平台提供了一种方法来获取 UDP 套接字上所有数据包的标头。这需要使用recvmsg 而不是recvfrom,因为标头是作为“辅助数据”而不是作为主接收缓冲区的一部分传递的。 Python 3.3 有recvmsg;早期版本没有,您将不得不使用ctypes 或一些第三方包装器(或构建您自己的包装器)。

【讨论】:

  • 谢谢,所以我理解使用 setsockopt 和 SO_REUSEADDR/PORT 是我的解决方案,但它不起作用。提出了一个关于这个的新问题......希望这次我能完成这个:)
  • @Jjang:正如我在答案中解释的那样,SO_REUSEADDR 在大多数平台上都不起作用,因为(由于各种不同的原因)数据包最终会被传递到错误的套接字。
猜你喜欢
  • 1970-01-01
  • 2012-02-12
  • 1970-01-01
  • 2016-01-23
  • 2012-10-05
  • 1970-01-01
  • 2012-03-06
  • 2014-07-25
  • 1970-01-01
相关资源
最近更新 更多