Chat server: Difference between revisions

Content added Content deleted
(→‎{{header|Go}}: added synchronization to clients, added logging using package log)
(Undo revision 187383 by Wswiss (talk) Does not ocompile)
Line 536: Line 536:


import (
import (
"bufio"
"os"
"bytes"
"flag"
"fmt"
"fmt"
"log"
"net"
"net"
"os"
"flag"
"sync"
"bufio"
"bytes"
)
)


// Quick and dirty error handling.
// A type for storing the connections.
func error_(err error, r int) {
type clientMap struct {
fmt.Printf("Error: %v\n", err)
// the map of names to connections

cl map[string]net.Conn
if r >= 0 {
// the mutex protects assoc and dissoc on cl ^
os.Exit(r)
mu sync.RWMutex
}
}
}

// A type for storing the connections.
type clientMap map[string]net.Conn


// A method that makes clientMap compatible with io.Writer, allowing it to be
// A method that makes clientMap compatible with io.Writer, allowing it to be
// used with fmt.Fprintf().
// used with fmt.Fprintf().
func (cm clientMap) Write(buf []byte) (n int, err error) {
func (cm clientMap) Write(buf []byte) (n int, err error) {
for _, c := range cm {
cm.mu.RLock()
defer cm.mu.RUnlock()
for _, c := range cm.cl {
// Write to each client in a seperate goroutine.
// Write to each client in a seperate goroutine.
go c.Write(buf)
go c.Write(buf)
}
}

n = len(buf)
n = len(buf)

return
return
}
}


// Check if a name exists; if it doesn't, add it.
// Check if a name exists; if it doesn't, add it.
func (cm clientMap) Assoc(name string, c net.Conn) bool {
func (cm clientMap) Add(name string, c net.Conn) bool {
for k := range cm {
cm.mu.Lock()
if name == k {
defer cm.mu.Unlock()
return false
if _, e := cm.cl[name]; e {
}
return false
}
}

cm[name] = c
cm[name] = c
return true
}


return true
func (cm clientMap) Dissoc(name string) {
cm.mu.Lock()
defer cm.mu.Unlock()
delete(cm.cl, name)
}
}


Line 589: Line 587:
func init() {
func init() {
// Initialize the map.
// Initialize the map.
clients = &clientMap{cl:make(map[string]net.Conn)}
clients = make(clientMap)
log.SetPrefix("!server> ")
}
}


Line 599: Line 596:
br := bufio.NewReader(c)
br := bufio.NewReader(c)


retry:
fmt.Fprintf(c, "Please enter your name: ")
fmt.Fprintf(c, "Please enter your name: ")


buf, err := br.ReadBytes('\n')
buf, err := br.ReadBytes('\n')
if err != nil {
if err != nil {
log.Println("Error: ", err)
error_(err, -1)
return
return
}
}
Line 610: Line 606:


if name == "" {
if name == "" {
fmt.Fprintln(c, "!!! invalid name !!!")
fmt.Fprintf(c, "!!! %v is invalid !!!\n", name)
goto retry
}
}


// Try to add the connection to the map.
// Try to add the connection to the map.
if !clients.Assoc(name, c) {
if !clients.Add(name, c) {
fmt.Fprintf(c, "!!! %s is not available !!!\n", name)
fmt.Fprintf(c, "!!! %v is not available !!!\n", name)
return
goto retry
}
}


// Send a message telling the clients who connected.
// Send a message telling the clients who connected.
fmt.Fprintf(clients, "+++ %s connected +++\n", name)
fmt.Fprintf(clients, "+++ %v connected +++\n", name)
// Send a disconnected message when the function returns.
// Send a disconnected message when the function returns.
defer fmt.Fprintf(clients, "--- %s disconnected ---\n", name)
defer fmt.Fprintf(clients, "--- %v disconnected ---\n", name)
// Remove the client from the list.
// Remove the client from the list.
defer clients.Dissoc(name)
defer delete(clients, name)


for {
for {
Line 649: Line 644:


// Send the message to all the clients.
// Send the message to all the clients.
fmt.Fprintf(clients, "%s\n", buf)
fmt.Fprintf(clients, "%v\n", string(buf))
}
}
}
}

// Flags. Use -help for usage info.
var (
port = flag.Int("port", 23, "Port to listen on")
help = flag.Bool("help", false, "Display this")
)


func main() {
func main() {
// Flags. Use -help for usage info.
var (
port int
help bool
)
flag.IntVar(&port, "port", 23, "Port to listen on")
flag.BoolVar(&help, "help", false, "Display this")
flag.Parse()
flag.Parse()


if *help {
if help {
flag.Usage()
flag.Usage()
return
return
Line 668: Line 664:


// Initialize a new listener.
// Initialize a new listener.
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
lis, err := net.Listen("tcp", fmt.Sprintf(":%v", port))
if err != nil {
if err != nil {
log.Fatalln("Error: ", err)
error_(err, 1)
}
}


Line 677: Line 673:
c, err := lis.Accept()
c, err := lis.Accept()
if err != nil {
if err != nil {
log.Println("Error: ", err)
error_(err, -1)
continue
continue
}
}