代码之家  ›  专栏  ›  技术社区  ›  R71

从同一个套接字读取TCP和UDP数据包

  •  1
  • R71  · 技术社区  · 6 年前

    我尝试在路由器中读取数据包,就像在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,我应该使用recv()还是recvfrom()。 UDP数据包?

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

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

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

    注: 一些给出的回答是不一致的,所以让我对我的问题划出一些界限。希望这些限制将有助于给出更具体的答案。

    (a)我的目标是用 仅限python套接字 . 因此,涉及tcpdump或tshark等的其他解决方案不在范围之内。

    (b)目标是 只有嗅探 对于传入的数据包。额外的细节,如包重新排序(对于面向连接的协议,如TCP)超出了范围,实际上它们是可以避免的开销。

    2 回复  |  直到 6 年前
        1
  •  2
  •   Mike Pennington ThiefMaster    6 年前

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

    同样地,这里的接收调用在协议上也不会有所不同,因为实际上 使用 TCP或UDP,您只需接收那些协议构建和解码的原始数据包。

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

    任何一个都可以。 recv() 将只返回实际的数据包数据,而 recvfrom 将返回数据以及有关数据包的元数据,包括从中接收数据的接口(以及在 struct sockaddr_ll 来自 packet(7) 手册页)。

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

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

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

    一般来说,大多数网络上的数据包限制在1514字节左右。这是因为在网络接口上配置的传统“MTU”(最大传输单元)是1500字节,并且通常会预先准备一个包含两个MAC地址(每个MAC地址6字节)和一个两字节EtherType的以太网头段。在交换机或路由器中,您还可以看到包含VLAN头的额外4字节头的数据包(IEEE802.1q)。(但是,一些网络内部使用的“巨型”数据包大小高达9K左右,用于特定目的。)

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

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

    操作系统将为您保留一个包队列。当然,它的大小是有限的,因为您无法以全线路速率(更不用说10GB或更高的网卡)跟上1GB的网卡。大小是以特定于系统的方式配置的。在Linux上——可能还有其他基于Unix的系统上——您可以调用 getsockopt 具有 SOL_SOCKET / SO_RCVBUF 了解可用的队列空间。

    在Linux上,至少可以使用 setsockopt 最多系统施加的最大值(其本身可以配置为 sysctl 设置)。

        2
  •  1
  •   Mike Pennington ThiefMaster    6 年前

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

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

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