Skip to content

Instantly share code, notes, and snippets.

@wkharold
Created December 4, 2013 04:54
Show Gist options
  • Save wkharold/7782557 to your computer and use it in GitHub Desktop.
Save wkharold/7782557 to your computer and use it in GitHub Desktop.
Relay a message from a client to all listeners
package main
import (
"flag"
"fmt"
"net"
"os"
"sync"
)
type Relay struct {
partners []Partner
sync.Mutex
}
func (r *Relay) Join(p Partner) {
r.Lock()
defer r.Unlock()
r.partners = append(r.partners, p)
}
func (r Relay) Send(p Partner, msg []byte) (int, error) {
r.Lock()
defer r.Unlock()
recipients := 0
for _, partner := range r.partners {
if partner != p {
partner.relaymsg <- msg
recipients++
}
}
return recipients, nil
}
type Partner struct {
k net.Conn
relaymsg chan []byte
}
func (p Partner) Participate() {
rdr := func(k net.Conn, c chan []byte) {
for {
data := make([]byte, 144)
_, err := k.Read(data)
if err != nil {
fmt.Println(err)
}
c <- data
}
}
clientmsg := make(chan []byte, 144)
go rdr(p.k, clientmsg)
for {
select {
case cm := <-clientmsg:
relay.Send(p, cm)
case rm := <-p.relaymsg:
p.k.Write(rm)
}
}
}
func ListenAndServe(addr string) error {
l, err := net.Listen("tcp", addr)
if err != nil {
return err
}
for {
k, err := l.Accept()
if err != nil {
return err
}
p := Partner{k, make(chan []byte)}
relay.Join(p)
go p.Participate()
}
return nil
}
var relay = Relay{}
func main() {
fladdr := flag.String("addr", "0.0.0.0:3006", "Relay listener address")
if err := ListenAndServe(*fladdr); err != nil {
fmt.Println(err)
os.Exit(1)
}
os.Exit(0)
}
@nerdatmath
Copy link

This code assumes that Read will always return the entire block, but that is not guaranteed by the io.Reader contract. Better to use io.ReadFull. Also Send blocks on the channel write to each Partner so all communication will hang up if any one client stops reading from the connection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment