Go基于协程实现TCP和UDP通信(net标准库)以及QUIC通信(quic库)

UDP实现

  • 不面向连接(dial和listen不会阻塞)
  • 不保证可靠交付
  • 单工(一个conn只能read或write)
  • 支持一对多
package main

import (
	"fmt"
	"log"
	"net"
	"time"
)

const (
	ip        = "127.0.0.1"
	port      = 9090
	clientNum = 3
)

func main() {
    
    
	for i := 0; i < clientNum; i++ {
    
    
		go func(i int) {
    
    

			serverAddr := fmt.Sprintf("%s:%d", ip, port)
			serverUdpAddr, err := net.ResolveUDPAddr("udp", serverAddr)
			if err != nil {
    
    
				log.Fatal(err)
			}
			connTo, err := net.DialUDP("udp", nil, serverUdpAddr)

			Addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", ip, port+i+1))
			if err != nil {
    
    
				log.Fatal(err)
			}
			connFrom, err := net.ListenUDP("udp", Addr)
			if err != nil {
    
    
				log.Fatal(err)
			}

			for {
    
    
				msg := []byte(fmt.Sprintf("hello from client%d", i))
				connTo.Write(msg)

				buf := make([]byte, 1024)
				connFrom.Read(buf)
				log.Printf("[Client%d]:%s", i, string(buf))

				time.Sleep(time.Second)
			}

		}(i)
	}

	addr := fmt.Sprintf("%s:%d", ip, port)
	udpAddr, err := net.ResolveUDPAddr("udp", addr)

	if err != nil {
    
    
		log.Fatal(err)
	}
	connFrom, err := net.ListenUDP("udp", udpAddr)
	if err != nil {
    
    
		log.Fatal(err)
	}

	connTo := make([]net.Conn, clientNum)
	for i := 0; i < clientNum; i++ {
    
    
		addr0 := fmt.Sprintf("%s:%d", ip, port+i+1)
		udpAddr0, err := net.ResolveUDPAddr("udp", addr0)
		if err != nil {
    
    
			log.Fatal(err)
		}
		connTo[i], _ = net.DialUDP("udp", nil, udpAddr0)
	}

	for {
    
    
		for i := 0; i < clientNum; i++ {
    
    
			buf := make([]byte, 1024)
			connFrom.Read(buf)
			log.Printf("[Server]:%s", string(buf))

			msg := []byte("hello from server")
			connTo[i].Write(msg)
		}
	}
}

TCP实现

  • 面向连接(dial和listen会阻塞等待)
  • 保证可靠交付
  • 双工(一个conn既能read也能write)
  • 只支持一对一
package main

import (
	"fmt"
	"log"
	"net"
	"time"
)

const (
	ip        = "127.0.0.1"
	port      = 9090
	clientNum = 3
)

func main() {
    
    
	for i := 0; i < clientNum; i++ {
    
    
		go func(i int) {
    
    
			serverAddr := fmt.Sprintf("%s:%d", ip, port)
			conn, err := net.Dial("tcp", serverAddr)
			if err != nil {
    
    
				log.Fatal(err)
			}
			defer conn.Close()

			for {
    
    
				buf := []byte(fmt.Sprintf("hello from client%d", i))
				conn.Write(buf)

				buf0 := make([]byte, 1024)
				conn.Read(buf0)
				log.Printf("[Client%d]:%s", i, string(buf0))
			}
		}(i)
	}

	serverAddr := fmt.Sprintf("%s:%d", ip, port)
	listener, err := net.Listen("tcp", serverAddr)
	if err != nil {
    
    
		log.Fatal(err)
	}
	defer listener.Close()
	conns := make([]net.Conn, clientNum)
	for i := 0; i < clientNum; i++ {
    
    
		conns[i], err = listener.Accept()
		if err != nil {
    
    
			log.Fatal(err)
		}
	}

	for {
    
    
		for i := 0; i < clientNum; i++ {
    
    
			buf0 := make([]byte, 1024)
			conns[i].Read(buf0)
			log.Printf("[Server]%s", string(buf0))

			buf := []byte("hello from server")
			conns[i].Write(buf)
		}
		time.Sleep(time.Second)
	}
}

QUIC实现

  • 面向连接(dial和listen会阻塞)
  • 保证可靠交付
  • 单工(一个conn只能read或write)
  • 只支持一对一

go get github.com/quic-go/quic-go (go version >=1.19)

const (
	ip        = "127.0.0.1"
	port      = 9090
	clientNum = 5
)

func main() {
    
    
	// Client
	for i := 0; i < clientNum; i++ {
    
    
		go func(i int) {
    
    
			connTo, err := quic.DialAddr(fmt.Sprintf("%s:%d", ip, port), genTlsConf(), nil)
			if err != nil {
    
    
				log.Fatal(err)
			}
			listener, err := quic.ListenAddr(fmt.Sprintf("%s:%d", ip, port+i+1), generateTLSConfig(), nil)
			if err != nil {
    
    
				log.Fatal(err)
			}
			defer listener.Close()
			connFrom, err := listener.Accept(context.Background())
			if err != nil {
    
    
				log.Fatal(err)
			}

			for {
    
    
				streamTo, err := connTo.OpenStream()
				if err != nil {
    
    
					log.Fatal(err)
				}
				streamTo.Write([]byte(fmt.Sprintf("hello from client%d", i)))
				streamTo.Close()

				streamFrom, err := connFrom.AcceptStream(context.Background())
				if err != nil {
    
    
					log.Fatal(err)
				}
				readBuf := make([]byte, 1024)
				streamFrom.Read(readBuf)
				streamFrom.Close()
				log.Printf("[Client%d]:%s", i, string(readBuf))

			}
		}(i)
	}

	// Server
	listener, err := quic.ListenAddr(fmt.Sprintf("%s:%d", ip, port), generateTLSConfig(), nil)
	if err != nil {
    
    
		log.Fatal(err)
	}
	defer listener.Close()
	connFrom := make([]quic.Connection, clientNum)
	connTo := make([]quic.Connection, clientNum)
	for i := 0; i < clientNum; i++ {
    
    
		connFrom[i], err = listener.Accept(context.Background())
		if err != nil {
    
    
			log.Fatal(err)
		}
	}
	for i := 0; i < clientNum; i++ {
    
    
		connTo[i], err = quic.DialAddr(fmt.Sprintf("%s:%d", ip, port+i+1), genTlsConf(), nil)
		if err != nil {
    
    
			log.Fatal(err)
		}
	}
	for {
    
    
		for i := 0; i < clientNum; i++ {
    
    
			streamFrom, err := connFrom[i].AcceptStream(context.Background())
			if err != nil {
    
    
				log.Fatal(err)
			}
			readBuf := make([]byte, 1024)
			streamFrom.Read(readBuf)
			streamFrom.Close()
			log.Println("[Server]:", string(readBuf))

			streamTo, err := connTo[i].OpenStream()
			if err != nil {
    
    
				log.Fatal(err)
			}
			writeBuf := []byte("hello from server...")
			streamTo.Write(writeBuf)
			streamTo.Close()

		}
		time.Sleep(time.Second)
	}
}

func genTlsConf() *tls.Config {
    
    
	tlsConf := &tls.Config{
    
    
		InsecureSkipVerify: true,
		NextProtos:         []string{
    
    "quic-echo-example"},
	}
	return tlsConf
}

func generateTLSConfig() *tls.Config {
    
    
	key, err := rsa.GenerateKey(rand.Reader, 1024)
	if err != nil {
    
    
		panic(err)
	}
	template := x509.Certificate{
    
    SerialNumber: big.NewInt(1)}
	certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
	if err != nil {
    
    
		panic(err)
	}
	keyPEM := pem.EncodeToMemory(&pem.Block{
    
    Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
	certPEM := pem.EncodeToMemory(&pem.Block{
    
    Type: "CERTIFICATE", Bytes: certDER})

	tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
	if err != nil {
    
    
		panic(err)
	}
	return &tls.Config{
    
    
		Certificates: []tls.Certificate{
    
    tlsCert},
		NextProtos:   []string{
    
    "quic-echo-example"},
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_46878177/article/details/130358511