2013-11-28 123 views
0

我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) 

基本上,我想将消息发送到通过sendSock回声服务器是UDP套接字,并从服务器通过recvSock收到答案这是一个原始的插座。

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

+0

当然,但你需要解码/编码UDP协议 – Torxed

+0

@Torxed:“确定”? 'recvSock'不会收到作为对他的'sendSock.sendto'的响应而发送的数据。他必须做一些完全不同的事情。 – abarnert

+1

同时,在你最后一个问题上,我说如果你想要任何人来帮助解决这个问题,你将不得不提供你的平台和版本。今天依然如此。对于任何不是特定于平台的问题的任何回答都将具有有限的用处;所有人都可以这样说:“有些平台可以做X,但是它们以不同的方式做,而其他平台不能做X,但可以做Y,这可能会或可能不是你需要的,而其他人不能做任何事情。 ,而且他们中的大多数已经改变了多年“,这不会帮助你编写你想写的代码。 – abarnert

回答

2

让我们回到了一下,因为写你的问题并没有多大意义,但我想我知道你想做什么。

当您创建一个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根据最初定义的BSD原始套接字协议准确解释您可以接受和不能接收的内容。幸运的是,大多数现代平台都超出了原来的BSD套接字协议 - 不幸的是,它们都以不同的方式来完成。

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

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

或者,更容易使用类似libpcap的东西,它具有用于数据包捕获的跨平台包装。

或者,更容易,使用类似scapy,其中有Python包装围绕libpcap和其他一切必要的,或wireshark,一个独立的程序,你可以从Python的自动化。通过这种方式可以轻松捕获将要传送到套接字的数据包,并解析标头以及您想要执行的其他任何事情。

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

+0

谢谢,所以我明白使用setsockopt和SO_REUSEADDR/PORT是我的解决方案,但它不工作。提出了一个新的问题...希望这一次我会完成这个:) – Jjang

+0

@Jjang:正如我在答案中解释,SO_REUSEADDR将无法在大多数平台上工作,因为(由于各种不同的原因)数据包将无论如何,最终都会被传递到错误的插槽。 – abarnert

相关问题