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

reading both tcp and udp packets from same socket

我正在尝试读取路由器中的数据包,例如 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 ...

相关问题()的回答说UDP和TCP数据的参数和方法不同(有人说recv是TCP的,recvfrom是UDP的,也有人说相反,类似的说 1024 作为 TCP 的缓冲区大小和更大的 UDP,有些人又说相反)。在我读取路由器的情况下,我没有不同的 TCP 和 UDP 套接字,所以我需要从同一个套接字读取这两个套接字,所以我对如何读取传入的数据包有点困惑。

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

(2) 调用 return 是一次一个数据包,还是在缓冲区填满后才 return?例如,如果我有一个 4096 字节的大缓冲区,并且传入的流式传输 2 个数据包每个都有 2400 字节,那么会在第一个数据包结束时立即调用 return,还是在填满后调用 return来自第二个数据包的缓冲区也是?

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

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

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

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

(b) objective 是为了 只嗅探 传入的数据包。其他细节,如数据包重新排序(对于像 TCP 这样的面向连接的协议)不在讨论范围内,实际上它们是可以避免的开销。

我认为你不应该那样做,因为 TCP 可以确保可靠性、顺序、流量控制和拥塞等各种事情。但是 UDP 不保证任何事情。

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

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

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

同样,这里的接收调用不会因协议而异,因为您实际上并未使用 TCP 或 UDP,您只是接收这些协议构建的原始数据包,并且解码。

(1) Should I use recv() or recvfrom(), if I want to read both TCP and UDP packets?

两者都行。 recv() 将 return 仅向您提供实际的数据包数据,而 recvfrom 将 return 向您提供数据以及有关数据包的元数据,包括数据来自的接口收到(以及 packet(7) 手册页中 struct sockaddr_ll 中定义的其他内容)。

(2) Do the calls return data one packet at a time, or do they return after the buffer is filled up? eg, if I have a large buffer of 4096 bytes, and the incoming streaming 2 packets have 2400 bytes each, will the call return as soon as the 1st packet ends, or will it return after filling up the buffer from the 2nd packet also?

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

(2a) same question, but if I have a smaller buffer of 2000 bytes. It is clear that on the 1st call I will get the first 2000 bytes of the 1st packet. But on the next call, will I get the last 400 bytes of the 1st packet, or the first 2000 bytes of the 2nd packet?

一般来说,大多数网络上的数据包都限制在 1514 字节左右。这是因为在网络接口上配置的传统 "MTU"(最大传输单元)是 1500 字节,通常以太网 header 包含两个 MAC 地址(每个 6 字节)加上一个 two-byte Ethertype 放在前面。在交换机或路由器中,您可能还会看到包含 VLAN header (IEEE 802.1Q) 的额外 4 字节 header 的数据包。 (但是,某些网络在内部使用 "jumbo" 大小高达 9K 的数据包用于特定目的。)

您还应该了解,在编写应用程序时,可以发送大于最大数据包大小的 UDP 数据报(或 TCP 缓冲区)。在那种情况下,OS 将它们分解成更小的块以进行发送(在将它们交给应用程序之前,它们在目标端是 re-assembled)。当您收到这样的原始数据包时,您会看到数据包处于 low-level 状态,可能是碎片化的。

(3) If I am delayed in making the next call, maybe because I was busy processing the 1st dataset, am I in danger of losing data, or will the OS keep its internal queue of the incoming packets to be given to me when I call the next time? If the OS keeps its internal queue, where can I find information about its size?

OS 将为您保留 queue 个数据包。大小当然是有限的,因为您无法以全线速率跟上 1Gb NIC(更不用说 10Gb 或更高的 NIC)了。大小以 system-specific 的方式配置。在 linux——可能还有其他 Unix-based 系统——你可以用 SOL_SOCKET / SO_RCVBUF 调用 getsockopt 来了解可用的 queue space .

在 linux 上,至少可以使用 setsockopt 设置大小,最大 system-imposed (它本身可以配置各种 sysctl 设置) .