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)

package main

import (
	"context"
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"github.com/quic-go/quic-go"
	"log"
	"math/big"
	"time"
)

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

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

func genServerTLSConfig() *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"},
	}
}

func genAddr(ip string, port int) string {
	return fmt.Sprintf("%s:%d", ip, port)
}

func main() {
	go func() {
		listener, err := quic.ListenAddr(genAddr(ip, port), genClientTlsConf(), 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(genAddr(ip, port+i+1), genServerTLSConfig(), 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.Printf("[Server]:get msg from client%d -- %s", i, readBuf)

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

	var err error
	connFrom, connTo := make([]quic.Connection, clientNum), make([]quic.Connection, clientNum)

	for i := 0; i < clientNum; i++ {
		connTo[i], err = quic.DialAddr(genAddr(ip, port), genClientTlsConf(), nil)
		if err != nil {
			log.Fatal(err)
		}
	}

	for i := 0; i < clientNum; i++ {
		listener, err := quic.ListenAddr(genAddr(ip, port+i+1), genServerTLSConfig(), nil)
		connFrom[i], err = listener.Accept(context.Background())
		if err != nil {
			log.Fatal(err)
		}
	}

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

			streamFrom, err := connFrom[i].AcceptStream(context.Background())
			if err != nil {
				log.Fatal(err)
			}
			readBuf := make([]byte, 1024)
			streamFrom.Read(readBuf)
			streamFrom.Close()
			log.Printf("[Client%d]:get msg from server -- %s", i, readBuf)
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
QUICTCP都是互联网传输议,它们各有优点和缺点,下面我来简单介绍一下: TCP的优点: 1. 可靠性高:TCP使用三次握手建立连接,通过序列号和确认应答机制保证数据的可靠传输。 2. 流控制:TCP通过滑动窗口机制进行流控制,能够避免数据发送速度过快导致网络拥塞、数据包丢失等问题。 3. 适应性强:TCP议能够自适应网络状况,根据网络拥塞情况进行拥塞控制,从而避免网络拥塞。 TCP的缺点: 1. 延时较高:由于TCP需要进行三次握手和流控制,所以在传输过程中会有一定的延时。 2. 传输效率较低:在网络拥塞的情况下,TCP会降低传输速度,从而降低传输效率。 3. 无法处理丢包:当数据包丢失时,TCP会进行重传,但是这会导致数据传输的效率降低。 QUIC的优点: 1. 延时低:QUIC使用UDP议,能够避免TCP进行三次握手和流控制导致的延时。 2. 传输效率高:QUIC使用多路复用技术和0-RTT特性,能够提高传输效率。 3. 安全性高:QUIC支持TLS加密,能够提供更高的安全性。 QUIC的缺点: 1. 兼容性差:QUIC是一个相对较新的议,目前不是所有的浏览器和服务器都支持该议。 2. 部署困难:由于QUIC没有被广泛部署,所以在实际应用中部署起来可能会比较困难。 3. 效果不稳定:由于QUIC是一个比较新的议,其效果在不同的网络环境和应用场景下可能会有所不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值