Skip to content

Instantly share code, notes, and snippets.

@subuk
Last active August 29, 2015 14:27
Show Gist options
  • Save subuk/cafc94b422a6f4a34170 to your computer and use it in GitHub Desktop.
Save subuk/cafc94b422a6f4a34170 to your computer and use it in GitHub Desktop.
Simple go chat
package main
import (
"net"
)
type Chat struct {
clients []*Client
joins chan net.Conn
}
func NewChat() *Chat {
chat := &Chat{
joins: make(chan net.Conn, 10),
}
go chat.run()
return chat
}
func (chat *Chat) Broadcast(message *Message) {
for _, client := range chat.clients {
if client == message.Client {
client.Write([]byte("\x1b[A\r"))
}
client.Write(message.Bytes())
}
}
func (chat *Chat) run() {
for conn := range chat.joins {
client := NewClient(conn.RemoteAddr().String(), conn, chat)
chat.clients = append(chat.clients, client)
}
}
package main
import (
"bufio"
"fmt"
"io"
"strings"
)
type Broadcaster interface {
Broadcast(*Message)
}
type Client struct {
name string
conn io.ReadWriter
chat Broadcaster
}
func NewClient(name string, conn io.ReadWriter, chat Broadcaster) *Client {
client := &Client{name, conn, chat}
chat.Broadcast(NewServerMessage(fmt.Sprintf("new connection from %s", client)))
go client.run()
return client
}
func (client *Client) run() {
reader := bufio.NewReader(client.conn)
for {
line, err := reader.ReadString('\n')
if err != nil {
text := fmt.Sprintf("client %s disconnected", client)
client.chat.Broadcast(NewServerMessage(text))
break
}
client.chat.Broadcast(NewMessage(strings.Trim(line, "\n"), client))
}
}
func (client *Client) String() string {
return client.name
}
func (client *Client) Write(text []byte) {
client.conn.Write(text)
}
package main
import (
"bytes"
"github.com/stretchr/testify/assert"
"testing"
)
type DummyChat struct {
messages chan *Message
}
func (c *DummyChat) Broadcast(m *Message) {
c.messages <- m
}
func TestClient_Works(t *testing.T) {
conn := bytes.NewBufferString("hello\n")
chat := &DummyChat{make(chan *Message, 1)}
client := NewClient("test", conn, chat)
msg := <-chat.messages
assert.Equal(t, "new connection from test", msg.Text)
assert.Nil(t, msg.Client)
msg = <-chat.messages
assert.Equal(t, "hello", msg.Text)
assert.Equal(t, client, msg.Client)
}
package main
import (
"net"
)
func main() {
chat := NewChat()
listener, _ := net.Listen("tcp", ":6666")
for {
conn, _ := listener.Accept()
chat.joins <- conn
}
}
package main
import (
"fmt"
)
type Message struct {
Client *Client
Text string
}
func NewServerMessage(text string) *Message {
return &Message{Text: text}
}
func NewMessage(text string, author *Client) *Message {
return &Message{author, text}
}
func (m *Message) String() string {
if m.Client == nil {
return fmt.Sprintf("[server] %s\n", m.Text)
}
return fmt.Sprintf("[%s] %s\n", m.Client, m.Text)
}
func (m *Message) Bytes() []byte {
return []byte(m.String())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment