Skip to content

Instantly share code, notes, and snippets.

@tsnow
Last active August 29, 2015 14:00
Show Gist options
  • Save tsnow/11385848 to your computer and use it in GitHub Desktop.
Save tsnow/11385848 to your computer and use it in GitHub Desktop.
Go chat server

Installation

go run server.go

Usage

telnet localhost 9000 #A
# separate terminal
telnet localhost 9000 #B

Then chat between the users:

#A
SexyT
#B
Josh
Yo
#A
hi

Which should result in the following output:

#A should see:
The room is empty.
Username:
You've logged on as SexyT.
Josh has joined.
Josh: Yo
SexyT: hi

#B should see:
Username:
Participants: SexyT
You've logged on as Josh.
Josh: Yo
SexyT: hi
package main
import (
"fmt"
"net"
"strings"
"sync"
)
type message struct {
user []byte
body []byte
}
type client struct {
user []byte
client net.Conn
close chan struct{}
}
type room struct {
m chan message
clients []*client
clientsMutex sync.Mutex // RWMutex might make more sense. Maybe.
}
func (c *client) sendMsg(b []byte) {
fmt.Fprintf(c.client, "%s %s\n", c.user, b)
}
func (c *client) hear(msg message) {
fmt.Fprintf(c.client, "%s: %s", msg.user, msg.body)
}
func (r *room) handleConnection(conn net.Conn) {
reqLen, err := fmt.Fprintf(conn, "Username: ")
if err != nil {
fmt.Println(err)
}
buf := make([]byte, 1024)
reqLen, err = conn.Read(buf)
user := client{
user: ([]byte)(strings.TrimSpace(string(buf[0:reqLen]))),
client: conn,
}
r.addClient(user)
fmt.Fprintf(conn, "You've logged on as %s\n", user.user)
for {
reqLen, err := conn.Read(buf)
if err != nil {
fmt.Println(err)
}
if strings.TrimSpace(string(buf[0:reqLen])) == "/quit" {
conn.Close()
return // can't write to this connection any more
}
m := message{
user: user.user,
body: buf[0:(reqLen - 1)],
}
r.m <- m
}
}
func (r *room) addClient(c client) {
//r.clientsMutex.Lock()
//defer r.clientsMutex.Unlock()
for i := range r.clients {
fmt.Fprintf(r.clients[i].client, "%s has joined \n", c.user)
}
fmt.Fprintf(c.client, "Participants: %#v.\n", r.clients)
r.clients = append(r.clients, &c)
}
func (r *room) broadcastMsg(m message) {
for i := range r.clients {
r.clients[i].hear(m)
}
}
func (r *room) broadcast() {
for {
select {
case i := <-r.m:
r.broadcastMsg(i)
}
}
}
func (r *room) listener() {
ln, err := net.Listen("tcp", ":9000")
if err != nil {
fmt.Println(err)
}
go r.broadcast()
for {
conn, err := ln.Accept()
if err != nil {
fmt.Println(err)
continue
}
go r.handleConnection(conn)
}
}
func main() {
oneRoomToRuleThemAll := room{
m: make(chan message),
}
oneRoomToRuleThemAll.listener()
}
@tsnow
Copy link
Author

tsnow commented May 5, 2014

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