package main import ( "github.com/gorilla/websocket" "log" "net/http" "sync" ) type Client struct { ip int socket *websocket.Conn channel chan string } func (c *Client) run() { log.Println("starting ", c.ip) var message SocketMessage for { select { case <-c.channel: log.Println("stopping ", c.ip) return default: c.read(&message) logger <- message broadcast <- Package{ sender: c.ip, message: message, } } } } func (c *Client) read(msg *SocketMessage) { err := c.socket.ReadJSON(&msg) if err != nil { log.Println(c, err) clients.remove(*c) } log.Println("read", msg, "from", c.ip) } func (c *Client) write(msg SocketMessage) { err := c.socket.WriteJSON(msg) if err != nil { log.Println(c, err) clients.remove(*c) } log.Println("wrote", msg, "to", c.ip) } type Clients struct { list map[int]*Client mux sync.Mutex } func (c *Clients) run() { log.Println("starting broadcaster") read := func() (message SocketMessage, ip int) { packet, ok := <-broadcast if !ok { log.Println("stopping broadcaster") return } return packet.message, packet.sender } for { msg, sender := read() for ip, client := range clients.list { if sender == ip { continue } client.write(msg) } } } func (c *Clients) add(client Client) { c.mux.Lock() defer c.mux.Unlock() c.list[client.ip] = &client log.Println("saved", client.ip) go client.run() } func (c *Clients) remove(client Client) { c.mux.Lock() defer c.mux.Unlock() _, ok := <-client.channel if ok { close(client.channel) } delete(c.list, client.ip) log.Println("removed", c.ip) } type History struct { log []SocketMessage mux sync.Mutex channel chan string } func (h *History) run() { log.Println("starting logger") for { message, ok := <-logger if !ok { log.Println("stopping logger") return } h.push(message) } } func (h *History) push(message SocketMessage) { h.mux.Lock() defer h.mux.Unlock() h.log = append(h.log, message) log.Println("logged ", message) } func (h *History) replay(client Client) { h.mux.Lock() defer h.mux.Unlock() for _, message := range h.log { client.write(message) } log.Println("replayed history for", client.ip) } type Package struct { sender int message SocketMessage } type SocketMessage struct { Tag string `json:"tag"` X int `json:"x"` Y int `json:"y"` } var upgrader = websocket.Upgrader{ ReadBufferSize: 1024, WriteBufferSize: 1024, CheckOrigin: func(r *http.Request) bool { return true }, } func init() { log.SetPrefix("") log.SetFlags(0) } var broadcast = make(chan Package, 10) var logger = make(chan SocketMessage, 10) var count = 0 var clients = &Clients{list: make(map[int]*Client)} var history = &History{log: make([]SocketMessage, 0)} func main() { history.push(SocketMessage{ Tag: "p", X: 1, Y: 2, }) http.HandleFunc("/connect", func(w http.ResponseWriter, r *http.Request) { log.Println("connect", count) socket, werr := upgrader.Upgrade(w, r, nil) if werr != nil { log.Println(werr) return } ip := count count++ client := Client{ ip: ip, socket: socket, channel: make(chan string)} history.replay(client) clients.add(client) }) go history.run() go clients.run() log.Fatal(http.ListenAndServe("localhost:8080", nil)) close(logger) close(broadcast) for _, client := range clients.list { close(client.channel) } }