mirror of
https://github.com/ossrs/srs.git
synced 2025-02-15 04:42:04 +00:00
When debugging the RTMP protocol, we can capture packets using tcpdump and then replay the pcap file. For example: ```bash cd ~/git/srs/trunk/3rdparty/srs-bench/pcap tcpdump -i any -w t.pcap tcp port 1935 go run . -f ./t.pcap -s 127.0.0.1:1935 ``` However, sometimes due to poor network conditions between the server and the client, there may be many retransmitted packets. In such cases, setting up a transparent TCP proxy that listens on port 1935 and forwards to port 19350 can be a solution: ```bash ./objs/srs -c conf/origin.conf cd 3rdparty/srs-bench/tcpproxy/ && go run main.go tcpdump -i any -w t.pcap tcp port 19350 ``` This approach allows for the implementation of packet dumping, multipoint replication, or the provision of detailed timestamps and byte information at the proxy. It enables the collection of debugging information without the need to modify the server. --------- `TRANS_BY_GPT4` --------- Co-authored-by: john <hondaxiao@tencent.com>
140 lines
3.3 KiB
Go
140 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/gopacket"
|
|
"github.com/google/gopacket/layers"
|
|
"github.com/google/gopacket/pcapgo"
|
|
"github.com/ossrs/go-oryx-lib/errors"
|
|
"github.com/ossrs/go-oryx-lib/logger"
|
|
)
|
|
|
|
func main() {
|
|
ctx := logger.WithContext(context.Background())
|
|
if err := doMain(ctx); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func trace(format string, args ...interface{}) {
|
|
fmt.Println(fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
func doMain(ctx context.Context) error {
|
|
var doRE, doTrace, help bool
|
|
var pauseNumber, abortNumber uint64
|
|
var filename string
|
|
var server string
|
|
flag.BoolVar(&help, "h", false, "whether show this help")
|
|
flag.BoolVar(&help, "help", false, "whether show this help")
|
|
flag.BoolVar(&doRE, "re", true, "whether do real-time emulation")
|
|
flag.BoolVar(&doTrace, "trace", true, "whether trace the packet")
|
|
flag.Uint64Var(&pauseNumber, "pause", 0, "the packet number to pause")
|
|
flag.Uint64Var(&abortNumber, "abort", 0, "the packet number to abort")
|
|
flag.StringVar(&filename, "f", "", "the pcap filename, like ./t.pcapng")
|
|
flag.StringVar(&server, "s", "", "the server address, like 127.0.0.1:1935")
|
|
|
|
flag.Parse()
|
|
|
|
if help {
|
|
flag.Usage()
|
|
os.Exit(0)
|
|
}
|
|
|
|
if filename == "" || server == "" {
|
|
flag.Usage()
|
|
os.Exit(1)
|
|
}
|
|
|
|
logger.Tf(ctx, "Forward pcap %v to %v, re=%v, trace=%v, pause=%v, abort=%v",
|
|
filename, server, doRE, doTrace, pauseNumber, abortNumber)
|
|
|
|
f, err := os.Open(filename)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "open pcap %v", filename)
|
|
}
|
|
defer f.Close()
|
|
|
|
var source *gopacket.PacketSource
|
|
if strings.HasSuffix(filename, ".pcap") {
|
|
r, err := pcapgo.NewReader(f)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "new reader")
|
|
}
|
|
source = gopacket.NewPacketSource(r, r.LinkType())
|
|
} else {
|
|
r, err := pcapgo.NewNgReader(f, pcapgo.DefaultNgReaderOptions)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "new reader")
|
|
}
|
|
source = gopacket.NewPacketSource(r, r.LinkType())
|
|
}
|
|
|
|
// TODO: FIXME: Should start a goroutine to consume bytes from conn.
|
|
conn, err := net.Dial("tcp", server)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "dial %v", server)
|
|
}
|
|
defer conn.Close()
|
|
|
|
var packetNumber uint64
|
|
var previousTime *time.Time
|
|
for packet := range source.Packets() {
|
|
packetNumber++
|
|
|
|
if packet.Layer(layers.LayerTypeTCP) == nil {
|
|
continue
|
|
}
|
|
|
|
ci := packet.Metadata().CaptureInfo
|
|
tcp, _ := packet.Layer(layers.LayerTypeTCP).(*layers.TCP)
|
|
payload := tcp.Payload
|
|
if len(payload) == 0 {
|
|
continue
|
|
}
|
|
if tcp.DstPort != 1935 && tcp.DstPort != 19350 {
|
|
continue
|
|
}
|
|
|
|
if pauseNumber > 0 && packetNumber == pauseNumber {
|
|
reader := bufio.NewReader(os.Stdin)
|
|
trace("#%v Press Enter to continue...", packetNumber)
|
|
_, _ = reader.ReadString('\n')
|
|
}
|
|
if abortNumber > 0 && packetNumber > abortNumber {
|
|
break
|
|
}
|
|
|
|
if _, err := conn.Write(payload); err != nil {
|
|
return errors.Wrapf(err, "write to %v", server)
|
|
}
|
|
|
|
if doRE {
|
|
if previousTime == nil {
|
|
previousTime = &ci.Timestamp
|
|
} else {
|
|
if diff := ci.Timestamp.Sub(*previousTime); diff > 100*time.Millisecond {
|
|
time.Sleep(diff)
|
|
previousTime = &ci.Timestamp
|
|
}
|
|
}
|
|
}
|
|
|
|
if doTrace {
|
|
trace("#%v TCP %v=>%v %v Len:%v",
|
|
packetNumber, uint16(tcp.SrcPort), uint16(tcp.DstPort),
|
|
ci.Timestamp.Format("15:04:05.000"),
|
|
len(payload))
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|