1//
2//  UDP ping command
3//  Model 1, does UDP work inline
4//
5
6//  this doesn't use ZeroMQ at all
7
8package main
9
10import (
11	"fmt"
12	"log"
13	"syscall"
14	"time"
15)
16
17const (
18	PING_PORT_NUMBER = 9999
19	PING_MSG_SIZE    = 1
20	PING_INTERVAL    = 1000 * time.Millisecond //  Once per second
21)
22
23func main() {
24
25	log.SetFlags(log.Lshortfile)
26
27	//  Create UDP socket
28	fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
29	if err != nil {
30		log.Fatalln(err)
31	}
32
33	//  Ask operating system to let us do broadcasts from socket
34	if err := syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1); err != nil {
35		log.Fatalln(err)
36	}
37
38	//  Bind UDP socket to local port so we can receive pings
39	if err := syscall.Bind(fd, &syscall.SockaddrInet4{Port: PING_PORT_NUMBER, Addr: [4]byte{0, 0, 0, 0}}); err != nil {
40		log.Fatalln(err)
41	}
42
43	buffer := make([]byte, PING_MSG_SIZE)
44
45	//  We use syscall.Select to wait for activity on the UDP socket.
46	//  We send a beacon once a second, and we collect and report
47	//  beacons that come in from other nodes:
48
49	rfds := &syscall.FdSet{}
50	timeout := &syscall.Timeval{}
51
52	//  Send first ping right away
53	ping_at := time.Now()
54
55	bcast := &syscall.SockaddrInet4{Port: PING_PORT_NUMBER, Addr: [4]byte{255, 255, 255, 255}}
56	for {
57		dur := int64(ping_at.Sub(time.Now()) / time.Microsecond)
58		if dur < 0 {
59			dur = 0
60		}
61		timeout.Sec, timeout.Usec = dur/1000000, dur%1000000
62		FD_ZERO(rfds)
63		FD_SET(rfds, fd)
64		_, err := syscall.Select(fd+1, rfds, nil, nil, timeout)
65		if err != nil {
66			log.Fatalln(err)
67		}
68
69		//  Someone answered our ping
70		if FD_ISSET(rfds, fd) {
71			_, addr, err := syscall.Recvfrom(fd, buffer, 0)
72			if err != nil {
73				log.Fatalln(err)
74			}
75			a := addr.(*syscall.SockaddrInet4)
76			fmt.Printf("Found peer %v.%v.%v.%v:%v\n", a.Addr[0], a.Addr[1], a.Addr[2], a.Addr[3], a.Port)
77		}
78		if time.Now().After(ping_at) {
79			//  Broadcast our beacon
80			fmt.Println("Pinging peers...")
81			buffer[0] = '!'
82			if err := syscall.Sendto(fd, buffer, 0, bcast); err != nil {
83				log.Fatalln(err)
84			}
85			ping_at = time.Now().Add(PING_INTERVAL)
86		}
87	}
88
89}
90
91func FD_SET(p *syscall.FdSet, i int) {
92	p.Bits[i/64] |= 1 << uint(i) % 64
93}
94
95func FD_ISSET(p *syscall.FdSet, i int) bool {
96	return (p.Bits[i/64] & (1 << uint(i) % 64)) != 0
97}
98
99func FD_ZERO(p *syscall.FdSet) {
100	for i := range p.Bits {
101		p.Bits[i] = 0
102	}
103}
104