【问题标题】:Possible to sit on the network and receive a TCP stream/UDP datagrams?可以坐在网络上并接收 TCP 流/UDP 数据报吗?
【发布时间】:2009-12-02 23:55:57
【问题描述】:

有没有人使用UDPSocket(用于UDP数据报)和InputStream(用于TCP流)的实现坐在数据包捕获接口(如jpcap)之上的工作?

考虑到 jpcap 中的回调 API,我想这不会太难做,但是那里有人已经做过了吗?这样做有什么问题吗(例如,我是否必须弄清楚如何自己重新组装 TCP 流?)

【问题讨论】:

    标签: java network-programming stream packet-capture


    【解决方案1】:

    我还没有做过这件特别的事情,但我在解析 C/C++ 中捕获的数据包方面做了很多工作。我不知道是否存在适用于此的 Java 库。

    基本上,您需要从 IP 开始,沿着协议栈向上工作。 pcap 数据以链路级标头开头,但我认为除了忽略非 IP 数据包之外,您不关心其中的太多内容。

    IP 最棘手的事情是重新组装分段的数据报。这是通过使用 Flags 字段和 Fragment Offset 字段中的 More Fragments 位来完成的,结合 Identification 字段来区分来自不同数据报的片段 然后使用 Protocol 字段来识别 TCP 和 UDP 数据包,并使用 Header Length 字段来查找相应标题的开头。

    对于 TCP 和 UDP,下一步是解复用,分离捕获的数据包流中的各种连接。两种协议都通过源和目标 IP 地址以及源和目标端口的 4 元组来识别连接(嗯,UDP 本身没有连接,但我没有更好的词),所以连接会是在所有 4 个值上匹配的数据包序列。

    完成后,对于 UDP,您就快完成了,除非您想检查校验和。 UDP 标头中的 Length 字段告诉您数据包有多长;标题减去 8 个字节,这就是你的数据。

    TCP 有点复杂,因为您确实必须重新组装流,这是使用标头中的序列号和长度来完成的。这两者的总和告诉您流中的下一个序列号。请记住,您正在跟踪两个方向的流量。

    (这比编写实际的 TCP 实现要容易得多,因为您必须实现 Nagle 算法和其他细节。)

    网上有很多关于标题格式的信息;对于初学者来说,谷歌“IP 标头”。像 Wireshark 这样的网络分析仪对于这项工作来说是必不可少的,因为它会向您展示您捕获的数据的外观。确实,由于 Wireshark 是开源的,您可能可以通过查看它的工作方式来了解很多

    【讨论】:

    • 我还要添加一件事:我发现将数据包标头拉入复制数据包布局的 C 样式结构中最容易。这需要一个预处理器指令来防止结构的内部填充,并且您必须记住分别使用 ntohs() 和 ntohl() 将 16 位和 32 位值转换为主机字节顺序。我不知道类似的方法在Java中是否可行,
    【解决方案2】:

    Tcp 重组可以通过JNetPcap 完成。这是一个完整的例子:

    final String SOME_PORT = 8888;
    
    StringBuilder errbuf = new StringBuilder();
    Pcap pcap = Pcap.openOffline("/dir/someFile.pcap", errbuf); //Can be replace with .openLive(...)
    
    if (pcap == null) {
        System.err.printf("Error: "+errbuf.toString());
        return;
    }
    
    //Handler that receive Tcp Event one by one
    AnalyzerListener<TcpStreamEvent> handler = new AnalyzerListener<TcpStreamEvent>() {
    
        @Override
        public void processAnalyzerEvent(TcpStreamEvent evt) {
            JPacket packet = evt.getPacket();
    
            Tcp tcp = new Tcp();
            if (packet.hasHeader(tcp)) {
    
                  //Limiting the analysis to a specific protocol
                  if (tcp.destination() == SOME_PORT || tcp.source() == SOME_PORT) {
                        String data = new String(tcp.getPayload());
                        System.out.println("Capture data:{"+data+"}");
                  }
            }
        }
    };
    
    TcpAnalyzer tcpAnalyzer = JRegistry.getAnalyzer(TcpAnalyzer.class);
    tcpAnalyzer.addTcpStreamListener(handler, null);
    
    //Starting the capture
    pcap.loop(Pcap.LOOP_INFINATE,  JRegistry.getAnalyzer(JController.class), null);
    

    【讨论】:

      猜你喜欢
      • 2013-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-08
      • 1970-01-01
      • 1970-01-01
      • 2011-09-16
      相关资源
      最近更新 更多