broadcast

package
v1.2.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 28, 2026 License: MIT Imports: 5 Imported by: 0

README

UDP Broadcast Discovery

基于 UDP Broadcast 的局域网服务发现库。

协议约定:

  • 客户端向固定 UDP 端口广播 JSON 查询:{"type":"query","service":"service-x"}
  • 服务端收到后向请求来源单播 JSON 响应:{"type":"response","service":"service-x","addr":"IP:PORT"}

作为依赖使用

服务端:

package main

import (
	"context"
	"log"

	broadcastserver "github.com/omalloc/contrib/net/broadcast/server"
)

func main() {
	if err := broadcastserver.ListenAndServe(context.Background(), broadcastserver.Config{
		Service:     "service-x",
		ServicePort: 8080,
		Meta: map[string]string{
			"version": "1.0.0",
		},
		Logger: log.Default(),
	}); err != nil {
		log.Fatal(err)
	}
}

客户端:

package main

import (
	"context"
	"fmt"
	"log"

	broadcastclient "github.com/omalloc/contrib/net/broadcast/client"
)

func main() {
	results, err := broadcastclient.Discover(context.Background(), broadcastclient.Config{
		Service: "service-x",
		Logger:  log.Default(),
	})
	if err != nil {
		log.Fatal(err)
	}

	for _, result := range results {
		fmt.Println(result.Addr, result.Meta)
	}
}

Documentation

Overview

Example (Broadcast)

Example with broadcast mode — server auto-detects interface, client uses subnet broadcast without explicit targets.

package main

import (
	"context"
	"fmt"
	"net"
	"time"

	broadcastclient "github.com/omalloc/contrib/net/broadcast/client"
	broadcastserver "github.com/omalloc/contrib/net/broadcast/server"
)

func main() {
	port := freeUDPPort()
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	go broadcastserver.ListenAndServe(ctx, broadcastserver.Config{
		Service:       "worker-node",
		DiscoveryPort: port,
		ServicePort:   8080,
		ServiceHost:   "127.0.0.1",
		Meta:          map[string]string{"role": "worker"},
	})

	time.Sleep(100 * time.Millisecond)

	results, err := broadcastclient.Discover(ctx, broadcastclient.Config{
		Service:       "worker-node",
		DiscoveryPort: port,
		Targets:       []*net.UDPAddr{{IP: net.ParseIP("127.0.0.1"), Port: port}},
		Timeout:       1 * time.Second,
	})
	if err != nil {
		fmt.Printf("discover error: %v\n", err)
		return
	}

	for _, r := range results {
		fmt.Printf("discovered %s at %s\n", r.Service, r.Addr)
	}

}

func freeUDPPort() int {
	conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		panic(err)
	}
	defer conn.Close()
	return conn.LocalAddr().(*net.UDPAddr).Port
}
Output:
discovered worker-node at 127.0.0.1:8080
Example (ExplicitTarget)

Example with explicit target — client and server communicate on localhost.

package main

import (
	"context"
	"fmt"
	"net"
	"sort"
	"time"

	broadcastclient "github.com/omalloc/contrib/net/broadcast/client"
	broadcastserver "github.com/omalloc/contrib/net/broadcast/server"
)

func main() {
	port := freeUDPPort()
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	// Start the server. It advertises "my-service" on port 9090.
	go broadcastserver.ListenAndServe(ctx, broadcastserver.Config{
		Service:       "my-service",
		DiscoveryPort: port,
		ServicePort:   9090,
		ServiceHost:   "127.0.0.1",
		Meta: map[string]string{
			"version": "1.0.0",
			"region":  "us-east",
		},
	})

	time.Sleep(100 * time.Millisecond)

	// Client discovers via explicit target.
	results, err := broadcastclient.Discover(ctx, broadcastclient.Config{
		Service:       "my-service",
		DiscoveryPort: port,
		Targets:       []*net.UDPAddr{{IP: net.ParseIP("127.0.0.1"), Port: port}},
		Timeout:       1 * time.Second,
	})
	if err != nil {
		fmt.Printf("discover error: %v\n", err)
		return
	}

	for _, r := range results {
		fmt.Printf("service=%s addr=%s\n", r.Service, r.Addr)
		keys := make([]string, 0, len(r.Meta))
		for k := range r.Meta {
			keys = append(keys, k)
		}
		sort.Strings(keys)
		for _, k := range keys {
			fmt.Printf("  meta[%s]=%s\n", k, r.Meta[k])
		}
	}

}

func freeUDPPort() int {
	conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
	if err != nil {
		panic(err)
	}
	defer conn.Close()
	return conn.LocalAddr().(*net.UDPAddr).Port
}
Output:
service=my-service addr=127.0.0.1:9090
  meta[region]=us-east
  meta[version]=1.0.0

Index

Examples

Constants

View Source
const (
	MessageTypeQuery    = "query"
	MessageTypeResponse = "response"
)
View Source
const DefaultDiscoveryPort = 5353

Variables

This section is empty.

Functions

func AddrWithPort

func AddrWithPort(ip net.IP, port int) string

func EnableBroadcast

func EnableBroadcast(conn *net.UDPConn) error

func EncodeMessage

func EncodeMessage(msg Message) ([]byte, error)

func IPv4ForPeer

func IPv4ForPeer(peer net.IP) (net.IP, error)

func IPv4ForPeerWithInterface added in v1.2.6

func IPv4ForPeerWithInterface(peer net.IP) (net.IP, string, error)

func ParseIPv4

func ParseIPv4(addr *net.UDPAddr) net.IP

func SubnetBroadcastTargets

func SubnetBroadcastTargets(port int) ([]*net.UDPAddr, error)

Types

type Message

type Message struct {
	Type    string            `json:"type"`
	Service string            `json:"service"`
	Addr    string            `json:"addr,omitempty"`
	Meta    map[string]string `json:"meta,omitempty"`
}

func DecodeMessage

func DecodeMessage(data []byte) (Message, error)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL