package main
import(
"bufio"
"fmt"
"log"
"net"
)
type client chan <- string
var(
entering = make(chan client)
leaving = make(chan client)
messages = make(chan string)
)
func broadcaster(){
clients := make(map[client]string)
for{
select{
case msg := <- messages:
for cli := range clients{
cli <- msg
}
case cli := <- entering:
clients[cli] = true
case cli := <- leaving:
delete(clients, cli)
close(cli)
}
}
}
func handleConn(conn net.Conn){
ch := make(chan string)
go clientWriter(conn, ch)
who := conn.RemoteAddr().String()
ch <- "You are " + who
messages <- who + " has arrived"
entering <- ch
input := bufio.NewScanner(conn)
for input.Scan(){
messages <- who + ": " + input.Text()
}
leaving <- ch
messages <- who + " has left"
conn.Close()
}
func clientWriter(conn net.Conn, ch <-chan string){
for msg := range ch{
fmt.Fprintln(conn, msg)
}
}
func main(){
listener, err := net.Listen("tcp", "localhost:8080")
if err != nil{
log.Fatal(err)
}
go broadcaster()
for{
conn, err := listener.Accept()
if err != nil{
log.Print(err)
continue
}
go handleConn(conn)
}
}
- 定义client为只能发送string类型的通道
- 定义entering和leaving为传输client类型的通道,嵌套定义通道,用于标记用户
- message为普通的string通道,用于发送和接收消息
- 主线程通过监听,接收每个用户,当有用户进入,为每个用户创建一个线程handleConn
- handleConn中通过创建string类型通道,作为entering和leaving的通道的通道的输入,标记用户进入和离开,同时通过message传输消息
- 在handleConn中通过给每个用户创造一个clientWriter线程,打印广播器的输出
- handleConn函数通过input.Scan阻塞,一直接收输入,直到连接断开
- 定义广播器broadcaster,使用clients记录用户,当有消息进入message通道,则广播这个消息给每个用户,每个用户都是可以发送string类型的通道,通过clientWriter接收这个通道并打印