Skip to content

Instantly share code, notes, and snippets.

@felixge
Created October 18, 2018 08:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save felixge/7bdc72e2e030c94413fde59aeb15c457 to your computer and use it in GitHub Desktop.
Save felixge/7bdc72e2e030c94413fde59aeb15c457 to your computer and use it in GitHub Desktop.
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"os"
"sync"
)
func main() {
if err := run(); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
}
func run() error {
l, err := net.Listen("tcp", ":1234")
if err != nil {
return err
}
chat := &Chat{}
for {
conn, err := l.Accept()
if err != nil {
return err
}
chat.Join(conn)
}
}
type Chat struct {
m sync.Mutex
conns []net.Conn
}
func (c *Chat) Join(conn net.Conn) {
c.m.Lock()
defer c.m.Unlock()
c.conns = append(c.conns, conn)
msg := fmt.Sprintf("%s joined - %d users online", conn.RemoteAddr(), len(c.conns))
go c.broadcast(msg)
go c.serve(conn)
}
func (c *Chat) serve(conn net.Conn) {
buf := bufio.NewReader(conn)
for {
var line []byte
for {
partial, prefix, err := buf.ReadLine()
line = append(line, partial...)
if err != nil {
c.leave(conn, err)
return
} else if !prefix {
break
}
}
msg := fmt.Sprintf("%s says: %s", conn.RemoteAddr(), line)
go c.broadcast(msg)
}
}
func (c *Chat) leave(conn net.Conn, err error) {
c.m.Lock()
defer c.m.Unlock()
newConns := make([]net.Conn, 0, len(c.conns)-1)
for _, existingConn := range c.conns {
if existingConn != conn {
newConns = append(newConns, existingConn)
}
}
c.conns = newConns
msg := fmt.Sprintf("%s left: %s - %d users online", conn.RemoteAddr(), err, len(c.conns))
go c.broadcast(msg)
}
func (c *Chat) broadcast(msg string) {
conns := c.cloneConns()
log.Println(msg)
for _, to := range conns {
_, err := io.WriteString(to, msg+"\n")
if err != nil {
log.Printf("failed to broadcast to: %s: %s", to.RemoteAddr(), err)
}
}
}
func (c *Chat) cloneConns() []net.Conn {
c.m.Lock()
defer c.m.Unlock()
conns := make([]net.Conn, len(c.conns))
copy(conns, c.conns)
return conns
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment