├── .gitignore ├── README.md ├── examples ├── listener │ └── main.go └── pinger │ └── main.go ├── main.go └── multicast ├── broadcaster.go └── listener.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Experiments in UDP Multicasting 2 | 3 | I have long been fascinated by broadcast networks. In the biological context, broadcast networks (and eavesdropping) are a central feature of anuran and insect choruses. 4 | 5 | In TCP/IP there is a similar mechanism provided for in the UDP broadcast and multicast. This package wraps Golang's UDP functions in the `net` package. 6 | 7 | ## Background 8 | 9 | The following is rephrased from [here](https://www.quora.com/What-is-the-difference-between-broadcasting-and-multicasting). 10 | 11 | **Broadcast** sends packets to all devices on a LAN. Unlike multicasting, there is no mechanism to limit the recipients of a broadcast: all packets go to all devices whether they want them or not. There is no mechanism to forward broadcasts between LANs. 12 | 13 | **Multicast** sends packets to all devices in a specified group. Membership in a group is set up when devices send "join" packets to an upstream router, and routers and switches keep track of this membership. When multicast packets arrive at a switch, they are only sent to devices or segments (such as WiFi) where at least one device wants them. Multicast can traverse the networks where it has been configured. 14 | 15 | ## Examples 16 | 17 | This package comes with some small command line utilities in the `examples` dir. 18 | 19 | In a terminal window, run the following from the root of this repository. 20 | 21 | ```bash 22 | $ go run examples/pinger/main.go 23 | # => Broadcasting to 239.0.0.0:9999 24 | ``` 25 | 26 | In a separate terminal window, run the following, also from the root of this repository. 27 | 28 | ```bash 29 | $ go run examples/listener/main.go 30 | # => Listening on 239.0.0.0:9999 31 | # 2017/04/12 12:53:24 13 bytes read from 192.168.1.129:51335 32 | # 2017/04/12 12:53:24 00000000 68 65 6c 6c 6f 2c 20 77 6f 72 6c 640a |hello, world.| 33 | # 34 | # 2017/04/12 12:53:25 13 bytes read from 192.168.1.129:51335 35 | # 2017/04/12 12:53:25 00000000 68 65 6c 6c 6f 2c 20 77 6f 72 6c 64 0a |hello, world.| 36 | ``` 37 | 38 | You may run as many instances of `listeners` or `pingers` as you would like. Concurrency is handled by the router (or switch). 39 | 40 | ## References 41 | 42 | The code in this repository is derived from [a Gist](https://gist.github.com/fiorix/9664255) created by [Andre Fiori](https://gist.github.com/fiorix) 43 | 44 | * https://www.quora.com/What-is-the-difference-between-broadcasting-and-multicasting 45 | * https://groups.google.com/forum/#!msg/golang-nuts/nbmYWwHCgPc/ZBw2uH6Bdi4J 46 | * https://en.wikipedia.org/wiki/Multicast_address 47 | * http://www.tldp.org/HOWTO/Multicast-HOWTO-2.html 48 | * https://support.mcommstv.com/hc/en-us/articles/202306226-Choosing-multicast-addresses-and-ports 49 | -------------------------------------------------------------------------------- /examples/listener/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "log" 7 | "net" 8 | "os" 9 | "time" 10 | 11 | "github.com/dmichael/go-multicast/multicast" 12 | "github.com/urfave/cli" 13 | ) 14 | 15 | const ( 16 | defaultMulticastAddress = "239.0.0.0:9999" 17 | ) 18 | 19 | func main() { 20 | app := cli.NewApp() 21 | 22 | app.Action = func(c *cli.Context) error { 23 | address := c.Args().Get(0) 24 | if address == "" { 25 | address = defaultMulticastAddress 26 | } 27 | fmt.Printf("Listening on %s\n", address) 28 | multicast.Listen(address, msgHandler) 29 | return nil 30 | } 31 | 32 | app.Run(os.Args) 33 | } 34 | 35 | func msgHandler(src *net.UDPAddr, n int, b []byte) { 36 | log.Println(n, "bytes read from", src) 37 | log.Println(hex.Dump(b[:n])) 38 | } 39 | 40 | func ping(addr string) { 41 | conn, err := multicast.NewBroadcaster(addr) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | 46 | for { 47 | conn.Write([]byte("hello, world\n")) 48 | time.Sleep(1 * time.Second) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/pinger/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "time" 8 | 9 | "github.com/dmichael/go-multicast/multicast" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | const ( 14 | defaultMulticastAddress = "239.0.0.0:9999" 15 | ) 16 | 17 | func main() { 18 | app := cli.NewApp() 19 | 20 | app.Action = func(c *cli.Context) error { 21 | address := c.Args().Get(0) 22 | if address == "" { 23 | address = defaultMulticastAddress 24 | } 25 | fmt.Printf("Broadcasting to %s\n", address) 26 | ping(address) 27 | return nil 28 | } 29 | 30 | app.Run(os.Args) 31 | } 32 | 33 | func ping(addr string) { 34 | conn, err := multicast.NewBroadcaster(addr) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | for { 40 | conn.Write([]byte("hello, world\n")) 41 | time.Sleep(1 * time.Second) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // https://gist.github.com/fiorix/9664255 2 | // https://en.wikipedia.org/wiki/Multicast_address 3 | // https://support.mcommstv.com/hc/en-us/articles/202306226-Choosing-multicast-addresses-and-ports 4 | 5 | package main 6 | 7 | import ( 8 | "encoding/hex" 9 | "log" 10 | "net" 11 | "time" 12 | 13 | "github.com/dmichael/go-multicast/multicast" 14 | ) 15 | 16 | const ( 17 | address = "239.0.0.0:9999" 18 | ) 19 | 20 | func main() { 21 | go ping(address) 22 | multicast.Listen(address, msgHandler) 23 | } 24 | 25 | func msgHandler(src *net.UDPAddr, n int, b []byte) { 26 | log.Println(n, "bytes read from", src) 27 | log.Println(hex.Dump(b[:n])) 28 | } 29 | 30 | func ping(addr string) { 31 | conn, err := multicast.NewBroadcaster(addr) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | 36 | for { 37 | conn.Write([]byte("hello, world\n")) 38 | time.Sleep(1 * time.Second) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /multicast/broadcaster.go: -------------------------------------------------------------------------------- 1 | package multicast 2 | 3 | import "net" 4 | 5 | // NewBroadcaster creates a new UDP multicast connection on which to broadcast 6 | func NewBroadcaster(address string) (*net.UDPConn, error) { 7 | addr, err := net.ResolveUDPAddr("udp4", address) 8 | if err != nil { 9 | return nil, err 10 | } 11 | 12 | conn, err := net.DialUDP("udp4", nil, addr) 13 | if err != nil { 14 | return nil, err 15 | } 16 | 17 | return conn, nil 18 | 19 | } 20 | -------------------------------------------------------------------------------- /multicast/listener.go: -------------------------------------------------------------------------------- 1 | package multicast 2 | 3 | import ( 4 | "log" 5 | "net" 6 | ) 7 | 8 | const ( 9 | maxDatagramSize = 8192 10 | ) 11 | 12 | // Listen binds to the UDP address and port given and writes packets received 13 | // from that address to a buffer which is passed to a hander 14 | func Listen(address string, handler func(*net.UDPAddr, int, []byte)) { 15 | // Parse the string address 16 | addr, err := net.ResolveUDPAddr("udp4", address) 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | 21 | // Open up a connection 22 | conn, err := net.ListenMulticastUDP("udp4", nil, addr) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | 27 | conn.SetReadBuffer(maxDatagramSize) 28 | 29 | // Loop forever reading from the socket 30 | for { 31 | buffer := make([]byte, maxDatagramSize) 32 | numBytes, src, err := conn.ReadFromUDP(buffer) 33 | if err != nil { 34 | log.Fatal("ReadFromUDP failed:", err) 35 | } 36 | 37 | handler(src, numBytes, buffer) 38 | } 39 | } 40 | --------------------------------------------------------------------------------