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

时间:2013-11-28 23:04:32

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

我用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向echo服务器发送消息,并通过recvSock从服务器接收答案,这是一个原始套接字。

有可能吗?如果是,那怎么样?代码不起作用。

1 个答案:

答案 0 :(得分:2)

让我们稍微回顾一下,因为写完你的问题没有多大意义,但我想我知道你要做什么。

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

当远程端响应时,它将向该源地址发送消息。内核收到该消息,并发现它的目标是UDP套接字绑定的地址。所以它将消息传递给该套接字。

这意味着recvfrom只能在该套接字上工作,而不是另一个套接字。只是在不受任何限制的不同套接字上调用recvfrom将无济于事。

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

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

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

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

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

或者,更简单的是,使用像('0.0.0.0', 12345)这样的东西,它具有用于数据包捕获的跨平台包装。

或者,更简单的是,使用类似libpcap的{​​{1}}和scapy以及其他所有必需的东西,或libpcap,这是一个可以从Python自动化的独立程序。通过这种方式,可以准确捕获将传递到套接字的数据包并解析标头以及您想要执行的所有其他操作。

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