Go 实现群聊天室- goroutine 和 channel 应用
经典的多线程程序群聊聊天室,利用goroutine和channel来实现。
实现这段程序,对理解go的goroutine和channel也非常有帮助。
服务端代码:
package main
import (
"net"
"fmt"
"os"
"bufio"
)
func main() {
listener, err := net.Listen("tcp", "localhost:8098")
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
}
go broadcaster()
for {
conn , err := listener.Accept()
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
}
go handleConn(conn)
}
}
type client chan<- string
var (
entering = make(chan client)
leaving = make(chan client)
message = make(chan string)
)
func broadcaster(){
clients := make(map[client]bool)
for {
select {
// 消息写到cli 注意这个cli是entering里的chan
// 而这个chan是 handleConn()里的ch
// 这就把msg通过clientWriter()写到了conn连接里
case msg := <-message :
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()
// 该消息通过clientWriter() 输出
ch <- "You are " + who
// 这条消息发给已经在entering的客户端里,不会发给刚进来的。
// 因为当前ch还没写到entering
message <- who + " has arrived"
// 此时才把当前ch放到entering
// 注意ch是作为通道类型放到entering的通道里,区别于msg
entering <- ch
input := bufio.NewScanner(conn)
for input.Scan() {
message <- who + " says: " + input.Text()
}
// for循环结束 表示退出
leaving <- ch
message <- who + " has left"
conn.Close()
}
/**
文案消息写到连接
*/
func clientWriter(conn net.Conn, ch <-chan string){
for msg := range ch {
fmt.Fprintln(conn, msg)
}
}
客户端代码:
package main
import (
"fmt"
"io"
"net"
"os"
)
func main() {
conn, err := net.Dial("tcp", "localhost:8098")
if err != nil {
fmt.Println("dial error")
}
go func() {
// 把连接返回值打印到屏幕
io.Copy(os.Stdout, conn)
}()
// 把标准输入描述符重定向到连接里
if _, err := io.Copy(conn, os.Stdin); err != nil {
fmt.Println("copy error")
}
fmt.Printf("%v conn", conn)
conn.Close()
}