package main import ( "encoding/hex" "fmt" "io" "log" "net" "sync" ) const ( client = `ownme.ipredator.se:10002` server = `ownme.ipredator.se:10000` ) type Direction int const ( ServerClient Direction = iota ClientServer ) func (d Direction) String() string { switch d { case ServerClient: return "Server → Client" case ClientServer: return "Client → Server" } return "unknown" } type Data struct { Round int Dir Direction Data []byte Size int } func dump(dir Direction, src, dst net.Conn, data chan Data) error { buf := make([]byte, 4096) for k := 1; ; k++ { n, err := src.Read(buf) if err != nil { if err == io.EOF { return nil } return fmt.Errorf("READ %v", err) } data <- Data{Round: k, Dir: dir, Data: buf[:n], Size: n} _, err = dst.Write(buf[:n]) if err != nil { return fmt.Errorf("WRITE %v", err) } } } func main() { var wg sync.WaitGroup srv, err := net.Dial("tcp", server) if err != nil { log.Fatal(err) } defer srv.Close() cnt, err := net.Dial("tcp", client) if err != nil { log.Fatal(err) } defer cnt.Close() data := make(chan Data) wg.Add(2) go func() { wg.Wait() close(data) }() go func(dir Direction) { if err := dump(dir, srv, cnt, data); err != nil { fmt.Println("ERROR", dir, err) } wg.Done() }(ServerClient) go func(dir Direction) { if err := dump(dir, cnt, srv, data); err != nil { fmt.Println("ERROR", dir, err) } wg.Done() }(ClientServer) for d := range data { fmt.Println(d.Round, d.Dir, d.Size, "bytes") fmt.Println(hex.Dump(d.Data)) } }