145 lines
2.9 KiB
Go
145 lines
2.9 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"io"
|
||
|
"log"
|
||
|
"net"
|
||
|
)
|
||
|
|
||
|
type Client struct {
|
||
|
Connection *net.Conn
|
||
|
Name string
|
||
|
Send chan string
|
||
|
}
|
||
|
|
||
|
type Message struct {
|
||
|
cli *Client
|
||
|
message string
|
||
|
}
|
||
|
|
||
|
type Server struct {
|
||
|
net.Listener
|
||
|
Clients []Client
|
||
|
NewClients chan Client
|
||
|
NewMessages chan Message
|
||
|
}
|
||
|
|
||
|
func (cli Client) Prompt() string {
|
||
|
return "<" + cli.Name + "> "
|
||
|
}
|
||
|
|
||
|
func (cli Client) SendMessages() {
|
||
|
for msg := range cli.Send {
|
||
|
io.WriteString(*cli.Connection, msg)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (cli Client) WaitMessage(server *Server) {
|
||
|
scanner := bufio.NewScanner(*cli.Connection)
|
||
|
io.WriteString(*cli.Connection, cli.Prompt())
|
||
|
for scanner.Scan() {
|
||
|
msg := Message{&cli, scanner.Text()}
|
||
|
if len(msg.message) > 0 {
|
||
|
log.Println("New message from", cli.Name, msg.message)
|
||
|
server.NewMessages <- msg
|
||
|
}
|
||
|
io.WriteString(*cli.Connection, cli.Prompt())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func NewServer(addr string) (*Server, error) {
|
||
|
ln, err := net.Listen("tcp", addr)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return &Server{
|
||
|
ln,
|
||
|
make([]Client, 0),
|
||
|
make(chan Client),
|
||
|
make(chan Message),
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func (server *Server) NewClient(conn *net.Conn) {
|
||
|
io.WriteString(*conn, "What is your name? ")
|
||
|
scanner := bufio.NewScanner(*conn)
|
||
|
for scanner.Scan() {
|
||
|
str := scanner.Text()
|
||
|
log.Println("New client", str)
|
||
|
cli := &Client{
|
||
|
conn,
|
||
|
str,
|
||
|
make(chan string),
|
||
|
}
|
||
|
server.NewClients <- *cli
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (server *Server) MessageDispatcher() {
|
||
|
for msg := range server.NewMessages {
|
||
|
log.Println("Dispatching message", msg.message, "from", msg.cli.Name)
|
||
|
for _, dest := range server.Clients {
|
||
|
if dest.Name != msg.cli.Name {
|
||
|
log.Println("Send message", msg.message, "from", msg.cli.Name, "to", dest.Name)
|
||
|
dest.Send <- "\n" + msg.cli.Prompt() + msg.message + "\n" + dest.Prompt()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (server *Server) CreateClients() {
|
||
|
for cli := range server.NewClients {
|
||
|
exists := false
|
||
|
for _, c := range server.Clients {
|
||
|
if c.Name == cli.Name {
|
||
|
exists = true
|
||
|
}
|
||
|
}
|
||
|
if exists {
|
||
|
io.WriteString(*cli.Connection, "Name already in use\n")
|
||
|
go server.NewClient(cli.Connection)
|
||
|
} else {
|
||
|
server.Clients = append(server.Clients, cli)
|
||
|
log.Println("Client ready to chat", cli.Name)
|
||
|
log.Println(len(server.Clients), "clients connected")
|
||
|
go cli.WaitMessage(server)
|
||
|
go cli.SendMessages()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// wait connections from clients
|
||
|
func (server *Server) handle() {
|
||
|
for {
|
||
|
conn, err := server.Accept()
|
||
|
if err != nil {
|
||
|
log.Println("Connection error", err)
|
||
|
}
|
||
|
go server.NewClient(&conn)
|
||
|
// TODO disconnect?
|
||
|
}
|
||
|
defer server.stop()
|
||
|
}
|
||
|
|
||
|
func (server *Server) stop() {
|
||
|
// TODO close connections
|
||
|
defer server.Close()
|
||
|
}
|
||
|
|
||
|
func (server *Server) start() {
|
||
|
log.Println("Starting server...")
|
||
|
go server.MessageDispatcher()
|
||
|
go server.CreateClients() // create client one at a time (race condition)
|
||
|
server.handle()
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
server, err := NewServer(":9000")
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
server.start()
|
||
|
}
|