Chat server: Difference between revisions
Content added Content deleted
(→{{header|Go}}: added synchronization to clients, added logging using package log) |
|||
Line 536: | Line 536: | ||
import ( |
import ( |
||
" |
"os" |
||
⚫ | |||
"flag" |
|||
"fmt" |
"fmt" |
||
"log" |
|||
"net" |
"net" |
||
" |
"flag" |
||
" |
"bufio" |
||
⚫ | |||
) |
) |
||
// Quick and dirty error handling. |
|||
⚫ | |||
func error_(err error, r int) { |
|||
⚫ | |||
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 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) { |
||
⚫ | |||
cm.mu.RLock() |
|||
defer cm.mu.RUnlock() |
|||
⚫ | |||
// 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) |
func (cm clientMap) Add(name string, c net.Conn) bool { |
||
for k := range cm { |
|||
cm.mu.Lock() |
|||
if name == k { |
|||
defer cm.mu.Unlock() |
|||
⚫ | |||
if _, e := cm.cl[name]; e { |
|||
} |
|||
⚫ | |||
} |
} |
||
cm[name] = c |
cm[name] = c |
||
⚫ | |||
⚫ | |||
⚫ | |||
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 = |
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 { |
||
error_(err, -1) |
|||
return |
return |
||
} |
} |
||
Line 610: | Line 606: | ||
if name == "" { |
if name == "" { |
||
fmt. |
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. |
if !clients.Add(name, c) { |
||
fmt.Fprintf(c, "!!! % |
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, "+++ % |
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, "--- % |
defer fmt.Fprintf(clients, "--- %v disconnected ---\n", name) |
||
// Remove the client from the list. |
// Remove the client from the list. |
||
defer |
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, "% |
fmt.Fprintf(clients, "%v\n", string(buf)) |
||
} |
} |
||
} |
} |
||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
func main() { |
func main() { |
||
⚫ | |||
⚫ | |||
port int |
|||
help bool |
|||
⚫ | |||
⚫ | |||
⚫ | |||
flag.Parse() |
flag.Parse() |
||
if |
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(":% |
lis, err := net.Listen("tcp", fmt.Sprintf(":%v", port)) |
||
if err != nil { |
if err != nil { |
||
error_(err, 1) |
|||
} |
} |
||
Line 677: | Line 673: | ||
c, err := lis.Accept() |
c, err := lis.Accept() |
||
if err != nil { |
if err != nil { |
||
error_(err, -1) |
|||
continue |
continue |
||
} |
} |