Wireguard-go can write but not able to read

Hi, not sure if this is the place to ask wireguard-go question, can someone help to explain why the read doesnot work but write is ok? thanks

package wireguard

import (
	"fmt"
	"syscall"
	"unsafe"

	"net/netip"

	"golang.zx2c4.com/wireguard/conn"
	"golang.zx2c4.com/wireguard/device"
	"golang.zx2c4.com/wireguard/tun"
	"golang.zx2c4.com/wireguard/tun/netstack"
)

func NewUDP(localAddr, remoteAddr, privateKey, publicKey string,
	mtu, localPort, remotePort int) (tun.Device, error) {
	tun, _, err := netstack.CreateNetTUN(
		[]netip.Addr{netip.MustParseAddr(localAddr)},
		[]netip.Addr{netip.MustParseAddr("8.8.8.8")},
		mtu,
	)
	if err != nil {
		return nil, err
	}

	dev := device.NewDevice(tun, conn.NewDefaultBind(),
		device.NewLogger(device.LogLevelVerbose, fmt.Sprint(localPort, ">")))
	err = dev.IpcSet(fmt.Sprintf(`private_key=%s
listen_port=%d
public_key=%s
allowed_ip=0.0.0.0/0
endpoint=127.0.0.1:%d
`, privateKey, localPort, publicKey, remotePort))
	if err != nil {
		return nil, err
	}
	err = dev.Up()
	if err != nil {
		return nil, err
	}

	return tun, nil
}
package wireguard

import (
	"errors"
	"fmt"
	"net"
	"testing"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
)

func TestUDP(t *testing.T) {
	exit := make(chan struct{})
	msgCh := make(chan string, 2)
	errCh := make(chan error, 2)
	localAddr := "127.0.0.1"
	serverAddr := "192.168.4.29"
	serverPort := 58120
	clientAddr := "192.168.4.28"
	clientPort := 58122
	mtu := 1200

	go func() {
		var (
			privateKey = "003ed5d73b55806c30de3f8a7bdab38af13539220533055e635690b8b87ad641"
			publicKey  = "f928d4f6c1b86c12f2562c10b07c555c5c57fd00f59e90c8d8d88767271cbf7c"
		)
		conn, err := NewUDP(serverAddr, clientAddr, privateKey, publicKey, mtu, serverPort, clientPort)
		if err != nil {
			errCh <- err
			return
		}

		data, err := ConstructUDP(localAddr, serverPort, localAddr, clientPort, []byte("Hello UDP Client"))
		if err != nil {
			errCh <- err
			return
		}

		time.Sleep(2 * time.Second)

		for {
			select {
			case <-exit:
				return
			default:
			}
			fmt.Println("++++server++++0")
			n, err := conn.Write(data, 0)
			if err != nil {
				errCh <- err
				return
			}
			if n == 0 {
				errCh <- errors.New("server write 0")
				return
			}
			fmt.Println("++++server++++1++", n)
			conn.Flush()

			var buf [2048]byte
			_, err = conn.Read(buf[0:], 0)
			if err != nil {
				errCh <- err
				return
			}
			fmt.Println("++++server++++2")
			msgCh <- fmt.Sprint("server> ", string(buf[0:]))

		}
	}()

	go func() {
		var (
			privateKey = "087ec6e14bbed210e7215cdc73468dfa23f080a1bfb8665b2fd809bd99d28379"
			publicKey  = "c4c8e984c5322c8184c72265b92b250fdb63688705f504ba003c88f03393cf28"
		)
		conn, err := NewUDP(clientAddr, serverAddr, privateKey, publicKey, mtu, clientPort, serverPort)
		if err != nil {
			errCh <- err
			return
		}

		data, err := ConstructUDP(localAddr, clientPort, localAddr, serverPort, []byte("Hello UDP Server"))
		if err != nil {
			errCh <- err
			return
		}

		time.Sleep(2 * time.Second)

		for {
			select {
			case <-exit:
				return
			default:
			}
			fmt.Println("++++client++++0")
			n, err := conn.Write(data, 0)
			if err != nil {
				errCh <- err
				return
			}
			if n == 0 {
				errCh <- errors.New("client write 0")
				return
			}
			fmt.Println("++++client++++1++", n)
			conn.Flush()

			var buf [2048]byte
			_, err = conn.Read(buf[0:], 0)
			if err != nil {
				errCh <- err
				return
			}
			fmt.Println("++++client++++2")
			msgCh <- fmt.Sprint("client> ", string(buf[0:]))

			time.Sleep(5 * time.Second)
			close(exit)
		}
	}()

	select {
	case msg := <-msgCh:
		t.Log(msg)
	case err := <-errCh:
		close(exit)
		t.Fatal(err)
	case <-exit:
	}

}

func ConstructUDP(srcIP string, srcPort int, dstIP string, dstPort int, data []byte) ([]byte, error) {
	sIP := net.ParseIP(srcIP)
	if sIP == nil {
		return nil, fmt.Errorf("non-ip target: %q", srcIP)
	}
	sIP = sIP.To4()
	if sIP == nil {
		return nil, fmt.Errorf("non-ipv4 target: %q", srcIP)
	}

	dIP := net.ParseIP(dstIP)
	if dIP == nil {
		return nil, fmt.Errorf("non-ip target: %q", dstIP)
	}
	dIP = dIP.To4()
	if dIP == nil {
		return nil, fmt.Errorf("non-ipv4 target: %q", dstIP)
	}

	ip := layers.IPv4{
		SrcIP:    sIP,
		DstIP:    dIP,
		Version:  4,
		TTL:      64,
		Protocol: layers.IPProtocolTCP,
	}

	sport := layers.TCPPort(srcPort)
	dport := layers.TCPPort(dstPort)
	tcp := layers.TCP{
		SrcPort: sport,
		DstPort: dport,
		Window:  1505,
		Urgent:  0,
		Seq:     11050,
		Ack:     0,
	}
	opts := gopacket.SerializeOptions{
		FixLengths:       true,
		ComputeChecksums: true,
	}

	tcp.SetNetworkLayerForChecksum(&ip)

	tcpPayloadBuf := gopacket.NewSerializeBuffer()
	payload := gopacket.Payload(data)
	if err := gopacket.SerializeLayers(tcpPayloadBuf, opts, &tcp, payload); err != nil {
		return nil, fmt.Errorf("failed to serialize: %v", err)
	}

	return tcpPayloadBuf.Bytes(), nil
}