从同一套接字读取tcp和udp数据包

时间:2018-07-17 13:41:24

标签: python sockets networking tcp udp

我正在尝试在路由器中读取数据包,例如python中的

# (skipping the exception handling code here)    
s = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(0x0003))
while True:
    p = s.recvfrom(2000)
    pkt = p[0]
    # process pkt here ...

一个相关问题的答案(36115971)说,UDP与TCP数据的参数和方法是不同的(有人说recv是用于TCP的,而recvfrom是用于UDP的,其他的则相反,有些人说的是1024)作为TCP的缓冲区大小,而对于UDP则更大,有些人反之亦然。就我在路由器中进行读取的情况而言,我没有用于TCP和UDP的不同套接字,因此我需要从同一套接字中读取这两个套接字,因此对于如何读取传入的数据包我有些困惑。

(1)如果要读取TCP UDP数据包,应该使用recv()还是recvfrom()?

(2)调用一次返回一个数据包,还是在缓冲区填满后返回?例如,如果我有一个4096字节的大缓冲区,而传入的2个流数据包每个都有2400字节,那么第一个数据包结束时,呼叫将立即返回,还是在填充了第二个数据包的缓冲区后又返回?

(2a)同样的问题,但是如果我有2000字节的较小缓冲区。很明显,在第一个调用中,我将获得第一个数据包的前2000个字节。但是在下一次通话时,我将获得第一个数据包的最后400个字节,还是第二个数据包的前2000个字节?

(3)如果我延迟进行下一个呼叫,则可能是因为我正忙于处理第一个数据集,是否有丢失数据的危险,或者操作系统将保留内部传入数据包的队列?我下次打电话时?如果操作系统保留其内部队列,我在哪里可以找到有关其大小的信息?

注意::某些给出的答复存在分歧,所以让我对我的问题作一些限制。希望这些限制将有助于给出更具体的答案。

(a)我的目标是仅使用 python套接字嗅探传入的数据包。因此,涉及tcpdump或tshark等的其他解决方案不在范围之内。

(b)目的是仅嗅探用于传入的数据包。数据包重排序(针对面向连接的协议,如TCP)的其他详细信息不在范围内,实际上,这些开销是可以避免的。

2 个答案:

答案 0 :(得分:2)

如果要从原始套接字读取数据包(如源代码所示),则可以轻松地从同一套接字读取所有数据包。确保这是您打算要做的。原始套接字用于进行数据包检查,以进行故障排除,法医,安全性或教育目的。您无法通过这种方式轻松地与另一个系统通信。

同样,此处的接收调用在协议上不会有所不同,因为您实际上并没有使用 TCP或UDP,您只是在接收那些协议构建和解码的原始数据包。

  

(1)如果我想同时读取TCP和UDP数据包,应该使用recv()还是recvfrom()?

任何一种都可以。 recv()将仅向您返回实际的数据包数据,而recvfrom将向您返回数据以及有关数据包的元数据,包括从中接收数据的接口(以及{ struct sockaddr_ll手册页中的{1}}。

  

(2)调用一次返回一个数据包,还是在缓冲区填满后返回?例如,如果我有一个4096字节的大缓冲区,而传入的2个流数据包每个都有2400字节,那么第一个数据包结束时,呼叫将立即返回,还是在填充了第二个数据包的缓冲区后又返回?

使用这样的原始套接字时,一次只能得到一个数据包。您将永远不会获得超过一个。如果您提供的缓冲区不够大,则该数据包将被截断(丢弃结尾字节)。

  

(2a)同样的问题,但是如果我有2000字节的较小缓冲区。很明显,在第一个调用中,我将获得第一个数据包的前2000个字节。但是在下一次通话时,我将获得第一个数据包的最后400个字节,还是第二个数据包的前2000个字节?

通常来说,大多数网络上的数据包限制为大约1514字节。这是因为在网络接口上配置的传统“ MTU”(最大传输单元)为1500字节,通常会在其中包含两个MAC地址(每个6字节)和一个2字节以太类型的以太网头。在交换机或路由器中,您可能还会看到数据包具有一个额外的4字节标头,其中包含VLAN标头(IEEE 802.1Q)。 (但是,某些网络内部出于特定目的使用最大约9K的“巨型”数据包。)

您还应该理解,在编写应用程序时,可以发送大于最大数据包大小的UDP数据报(或TCP缓冲区)。在这种情况下,操作系统会将它们分解成较小的块以进行发送(它们在移交给应用程序之前在目标端重新组装)。当您收到这样的原始数据包时,您会看到这些数据包处于低水平(可能是零散的)状态。

  

(3)如果我延迟进行下一个呼叫,则可能是因为我正忙于处理第一个数据集,是否有丢失数据的危险,或者操作系统将保留内部传入数据包的队列?我下次打电话时?如果操作系统保留其内部队列,我在哪里可以找到有关其大小的信息?

操作系统将为您保留数据包队列。大小当然是有限的,因为您无法以全线速跟上1Gb NIC(更不用说10Gb或更高的NIC)了。大小以系统特定的方式配置。在linux上(可能还有其他基于Unix的系统上),可以用packet(7)调用getsockopt来了解可用的队列空间。

在Linux上,至少可以使用SOL_SOCKET / SO_RCVBUF来设置大小,最高可以是系统强加的最大值(可以使用各种setsockopt设置本身来配置大小)。

答案 1 :(得分:1)

我认为您不应该这样做,因为TCP确保了可靠性,排序,流控制和拥塞等各种功能。但是UDP不能保证任何事情。

这些参数是在操作系统创建套接字时定义的。这就是为什么我认为您不能做到的意思。

打开两个不同的套接字,一个本地UDP套接字和一个本地TCP套接字。