You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用tcpdump+Python Socket实时传输网络流量的异常问题求助

实时TCPDUMP数据包远程传输问题排查与修复

我需要在一台机器上用tcpdump抓包,实时逐个把数据包传到另一台设备,且不能存本地文件。我写了发送端和接收端的Python脚本,但接收端要么丢包,要么数据包损坏。测试直接存本地文件是正常的,求解决方法。

原发送端代码

HOST = "192.168.xx.xx"
PORT = 65432

command = ['sudo', 'tcpdump', '-c', '1000', '-i', 'br0', '--packet-buffered', '-w', '-']
process = subprocess.Popen(command, stdout=subprocess.PIPE)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)
sock.connect(server_address)

for ts, packet_data in dpkt.pcap.Reader(process.stdout):
    eth_packet = dpkt.ethernet.Ethernet(packet_data)
    packet_bytes = eth_packet.pack()
    eth_packet.time = ts
    sock.sendall(packet_bytes)

sock.close()

原接收端代码

HOST = "192.168.xx.xx"
PORT = 65432
BUF = 4096

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)
sock.bind(server_address)
sock.listen(1)

print('Waiting for a connection...')
connection, sender_address = sock.accept()

while True:
    # Receive data from the socket
    data = connection.recv(BUF)
    if not data:
        break
    # Parse the received data as a packet with dpkt and write it to the pcap file
    packet = dpkt.ethernet.Ethernet(data)
    pcap_writer.writepkt(packet)

# Close the PcapWriter and the socket
pcap_file.close()
connection.close()

问题根源

  1. TCP粘包/拆包:TCP是流式协议,recv(BUF)可能一次收到多个数据包片段,或一个数据包被拆成多次接收,直接解析会导致数据损坏。
  2. 冗余处理错误:发送端用dpkt.pcap.Reader解析后重新pack(),完全没必要,反而可能引入格式错误,直接转发原始packet_data即可。
  3. 无数据包边界:接收端无法区分数据包的起始/结束位置,无法判断什么时候是完整的一个包。
  4. 时间戳丢失:发送端没有传递数据包的时间戳,导致生成的pcap文件时间信息错误。

修复方案

方案1:直接转发原始PCAP数据流(最高效)

发送端直接把tcpdump输出的原始PCAP格式数据转发,无需解析,避免处理错误;接收端直接写入文件,和本地抓包格式完全一致。

修改后的发送端

import socket
import subprocess

HOST = "192.168.xx.xx"
PORT = 65432

# tcpdump直接输出PCAP格式到stdout,--packet-buffered确保实时输出
command = ['sudo', 'tcpdump', '-c', '1000', '-i', 'br0', '--packet-buffered', '-w', '-']
process = subprocess.Popen(command, stdout=subprocess.PIPE)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)
sock.connect(server_address)

try:
    # 直接转发tcpdump的原始输出
    while True:
        chunk = process.stdout.read(4096)
        if not chunk:
            break
        sock.sendall(chunk)
finally:
    sock.close()
    process.wait()

修改后的接收端

import socket

HOST = "192.168.xx.xx"
PORT = 65432

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)
sock.bind(server_address)
sock.listen(1)

print('Waiting for a connection...')
connection, sender_address = sock.accept()

try:
    # 直接将数据流写入PCAP文件
    with open('received.pcap', 'wb') as pcap_file:
        while True:
            data = connection.recv(4096)
            if not data:
                break
            pcap_file.write(data)
finally:
    connection.close()
    sock.close()

方案2:带边界标识的逐个数据包传输(需实时处理场景)

如果需要对数据包做实时处理,必须用长度前缀标识每个数据包的边界,同时传递时间戳。

修改后的发送端

import socket
import subprocess
import struct
import dpkt

HOST = "192.168.xx.xx"
PORT = 65432

command = ['sudo', 'tcpdump', '-c', '1000', '-i', 'br0', '--packet-buffered', '-w', '-']
process = subprocess.Popen(command, stdout=subprocess.PIPE)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)
sock.connect(server_address)

try:
    for ts, packet_data in dpkt.pcap.Reader(process.stdout):
        # 先发送4字节大端序的数据包长度
        sock.sendall(struct.pack('!I', len(packet_data)))
        # 发送数据包内容
        sock.sendall(packet_data)
        # 发送8字节大端序的时间戳(double类型)
        sock.sendall(struct.pack('!d', ts))
finally:
    sock.close()
    process.wait()

修改后的接收端

import socket
import struct
import dpkt

HOST = "192.168.xx.xx"
PORT = 65432

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)
sock.bind(server_address)
sock.listen(1)

print('Waiting for a connection...')
connection, sender_address = sock.accept()

try:
    # 初始化PCAP Writer,链路类型和抓包接口一致(以太网为DLT_EN10MB)
    with open('received.pcap', 'wb') as pcap_file:
        pcap_writer = dpkt.pcap.Writer(pcap_file, linktype=dpkt.pcap.DLT_EN10MB)
        
        while True:
            # 接收4字节的数据包长度
            len_data = connection.recv(4)
            if not len_data:
                break
            packet_len = struct.unpack('!I', len_data)[0]
            
            # 接收完整的数据包内容
            packet_data = b''
            while len(packet_data) < packet_len:
                chunk = connection.recv(packet_len - len(packet_data))
                if not chunk:
                    break
                packet_data += chunk
            if len(packet_data) != packet_len:
                break  # 数据包不完整,终止
            
            # 接收时间戳
            ts_data = connection.recv(8)
            if not ts_data:
                break
            ts = struct.unpack('!d', ts_data)[0]
            
            # 写入PCAP文件
            pcap_writer.writepkt(packet_data, ts=ts)
finally:
    connection.close()
    sock.close()

最佳实践建议

  • 若仅需远程抓包保存,优先用方案1,直接转发原始PCAP数据流,效率最高且无格式错误。
  • 需实时处理数据包时,必须用长度前缀解决TCP粘包问题,确保每个数据包完整接收。
  • 抓包时保留--packet-buffered参数,避免tcpdump缓冲区堆积导致延迟或丢包。
  • 尽量减少发送端的数据包处理环节,直接转发原始数据能最大程度保证完整性。

内容的提问来源于stack exchange,提问作者Firouziam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 15:47:33