返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

udp

发布于 2024-10-12 19:15:52 字数 2387 浏览 0 评论 0 收藏 0

用户资料包协议 (UDP)协议是无连接(connectionless)的,不关心后续分组(datagram)状态。

  • 协议不可靠,不保证数据报不丢失、不延迟、不错序传输。
  • 客户端不关心服务端是否处于监听或者已经准备好接收数据。
  • 无连接协议可方便实现一对多,或多对一通信。

实际上作为 TCP、UDP 基础的 IP 协议也是无连接的,TCP 分组(segment)是通过自身来实现可靠性。
多数时候 UDP 要比 TCP 更快。

为便于阅读,示例代码做了精简(未对错误做处理等)。

// server.go

package main

import (
	"log"
	"net"
	"runtime"
	"time"
)

const (
	network = "udp"
	address = ":9999"
	bufsize = 500
	timeout = time.Second * 10
)

func server() {
	ready := make(chan struct{})

	go func() {
		addr, _ := net.ResolveUDPAddr(network, address)
		conn, _ := net.ListenUDP(network, addr)
		defer conn.Close()

		close(ready)

		// workers
		sema := make(chan struct{}, runtime.GOMAXPROCS(0))
		for {
			sema <- struct{}{}
			go func() {
				defer func(){ <-sema }()
				handle(conn)
			}()
		}
	}()

	<-ready
}

func handle(conn *net.UDPConn) {
	buf := make([]byte, bufsize)
	for {
		conn.SetReadDeadline(time.Now().Add(timeout))
		n, addr, err := conn.ReadFromUDP(buf)

		if n > 0 {
			conn.WriteToUDP(buf[:n], addr)
		}

		if err != nil {
			if e, ok := err.(net.Error); ok && e.Timeout() {
				continue
			}
			log.Fatalln(err)
		}
	}	
}
// client.go

package main

import (
	"bytes"
	"fmt"
	"log"
	"math/rand"
	"net"
	"time"
)

func client() {
	addr, _ := net.ResolveUDPAddr(network, address)
	conn, _ := net.DialUDP(network, nil, addr)
	defer conn.Close()

	sbuf, rbuf := make([]byte, 10), make([]byte, 10)
	for {
		// random data.
		rand.Read(sbuf)

		// send
		n, err := conn.Write(sbuf)
		if err != nil {
			log.Fatalln(err, n)
		}

		// recv
		n, _, err = conn.ReadFromUDP(rbuf)

		// verify data.
		if !bytes.Equal(sbuf, rbuf) {
			log.Fatalln("sbuf != rbuf")
		}

		if n > 0 {
			fmt.Printf(" > %v, % x\n", conn.LocalAddr(), rbuf[:n])
		}

		if err != nil {
			log.Fatalln(err)
		}

		time.Sleep(time.Millisecond * 50)
	}
}
// main.go

package main

import (
	"log"
)

func init() {
	log.SetFlags(log.Ltime)
}

func main() {
	server()

	for i := 0; i < 10; i++ {
		go client()
	}		

	select{}
}

数据包

考虑到 UDP 协议的不可靠特性,所以包大小最好限制在 MTU 范围内。
需要减去相关协议头大小。

Internet: MTU(576) - IPv4(20) - UDP(8) = 548
Intranet: MTU(1500) - PPPoE(8) - IPv4(20) - UDP(8) = 1464

鉴于 IP 头最大值是 60,因此建议的 Internet 安全上限为 508 字节。
同理,Intranet 合理阈值 1424 字节。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文