Skip to content

Instantly share code, notes, and snippets.

@fengxsong
Created October 3, 2016 03:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fengxsong/19e9edf9e4085deb5d27930896063a93 to your computer and use it in GitHub Desktop.
Save fengxsong/19e9edf9e4085deb5d27930896063a93 to your computer and use it in GitHub Desktop.
chat server or client.
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
"log"
"net"
"os"
"strings"
)
type Client struct {
token string
localWeather string
conn net.Conn
}
func (c *Client) Echo() {
for {
if c.token != "" {
break
}
nameRd := bufio.NewReader(os.Stdin)
fmt.Print("please named yourself: ")
username, _ := nameRd.ReadString('\n')
username = strings.TrimSpace(username)
if username == "" {
continue
}
c.token = username
c.conn.Write([]byte(username))
}
log.Println("start chat...")
for {
rd := bufio.NewReader(os.Stdin)
fmt.Printf(">> ")
msg, _ := rd.ReadString('\n')
msg = strings.TrimSpace(msg)
if msg == "" {
continue
} else if msg == "?weather" {
log.Println(c.Weather())
continue
}
_, err := c.conn.Write([]byte(msg))
if err != nil {
log.Fatalln(err)
}
if msg == "?q" {
c.conn.Close()
fmt.Println("ByeBye..")
os.Exit(0)
}
}
}
func (c *Client) Weather() string {
if strings.TrimSpace(c.localWeather) != "" {
return c.localWeather
}
c.localWeather = "getWeatherFromHTTPAPI"
return c.localWeather
}
func (c *Client) Start(raddr string) {
if c.conn == nil {
rAddr, err := net.ResolveTCPAddr("tcp4", raddr)
if err != nil {
log.Fatalln(err)
}
c.conn, err = net.DialTCP("tcp", nil, rAddr)
if err != nil {
log.Fatalln(err)
}
}
log.Println("Connecting to chat server...", raddr)
go c.Echo()
buf := make([]byte, 1024)
for {
n, err := c.conn.Read(buf)
if err != nil {
log.Fatalln(err)
}
log.Println(string(buf[:n]))
}
}
type Server struct {
listenAddr string
clients map[string]*Client
joins chan *M
sends chan *M
lefts chan *M
}
type M struct {
c *Client
act string
message string
}
func (s *Server) broadcast(m *M) {
for t, c := range s.clients {
_, err := c.conn.Write([]byte(m.act + ": " + m.message))
if err != nil {
log.Println(err)
delete(s.clients, t)
}
}
}
func (s *Server) Handler(c *Client) {
raddr := c.conn.RemoteAddr().String()
buft := make([]byte, 128)
n, _ := c.conn.Read(buft)
c.token = string(buft[:n])
s.joins <- &M{c, "join", fmt.Sprintf("connection from %s as %s", raddr, c.token)}
s.clients[c.token] = c
buf := make([]byte, 1024)
for {
n, err := c.conn.Read(buf)
if err != nil {
log.Fatalln(err)
}
message := string(buf[:n])
if message == "?q" {
defer c.conn.Close()
s.lefts <- &M{c, "left", fmt.Sprintf("%s disconnect", c.token)}
delete(s.clients, c.token)
return
} else if message == "?users" {
c.conn.Write([]byte(s.CurUsers()))
continue
}
s.sends <- &M{c, "", fmt.Sprintf("%s says %s", c.token, message)}
}
}
func (s *Server) CurUsers() string {
buf := bytes.Buffer{}
buf.WriteString("current users: ")
for t := range s.clients {
buf.WriteString(t + ", ")
}
return buf.String()
}
func (s *Server) BroadCast() {
for {
select {
case join := <-s.joins:
s.broadcast(join)
case send := <-s.sends:
s.broadcast(send)
case left := <-s.lefts:
s.broadcast(left)
}
}
}
func (s *Server) Start() {
laddr, err := net.ResolveTCPAddr("tcp4", s.listenAddr)
if err != nil {
log.Fatalln(err)
}
l, err := net.ListenTCP("tcp", laddr)
if err != nil {
log.Fatalln(err)
}
go s.BroadCast()
log.Println("Listening on ", s.listenAddr)
for {
conn, err := l.Accept()
if err != nil {
log.Println(err)
continue
}
c := &Client{conn: conn}
go s.Handler(c)
}
}
func main() {
flag.Usage = func() {
log.Fatalln("%s -role server/client -addr :8080", os.Args[0])
}
role := flag.String("role", "server", "play role, server or client")
addr := flag.String("addr", ":8080", "Listening addr or remote addr")
flag.Parse()
if *role == "server" {
s := &Server{
listenAddr: *addr,
clients: make(map[string]*Client),
joins: make(chan *M, 1<<10),
sends: make(chan *M, 1<<10),
lefts: make(chan *M, 1<<10),
}
s.Start()
} else if *role == "client" {
c := &Client{}
c.Start(*addr)
} else {
flag.Usage()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment