功能介绍:让用户通过服务端广播出去文本消息
goroutine使用介绍:这个程序中有四种goroutine。main和broadcaster各自是一个goroutine实例,每一个客户端的连接都会有一个handleConn和clientWriter的goroutine。
协议:tcp
broadcaster的goroutine。他的内部变量clients会记录当前建立连接的客户端集合。其记录的内容是每一个客户端的消息发出channel的"资格"信息.
broadcaster监听来自全局的entering和leaving的channel来获知客户端的到来和离开事件。当其接收到其中的一个事件时,会更新clients集合,当该事件是离开行为时,它会关闭客户端的消息发出channel。broadcaster也会监听全局的消息channel,所有的客户端都会向这个channel中发送消息。当broadcaster接收到什么消息时,就会将其广播至所有连接到服务端的客户端。
//服务端实现
package main
import (
"bufio"
"fmt"
"log"
"net"
)
type client chan<- string //an outgoing message channel
var (
entering = make(chan client)
leaving = make(chan client)
messages = make(chan string) // all common messages
)
func boardcast() {
clients := make(map[client]bool)
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 clientwriter(conn net.Conn, ch <-chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg)
}
}
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 main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
go boardcast()
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
continue
}
go handleConn(conn)
}
}
//客户端实现
每一个客户端的goroutine。handleConn函数会为它的客户端创建一个消息发出channel并通过entering channel来通知客户端的到来。然后它会读取客户端发来的每一行文本,并通过全局的消息channel来将这些文本发送出去,并为每条消息带上发送者的前缀来标明消息身份。当客户端发送完毕后,handleConn会通过leaving这个channel来通知客户端的离开并关闭连接。
package main
import (
"io"
"log"
"net"
"os"
)
func mustCopy(dst io.Writer, src io.Reader) {
if _, err := io.Copy(dst, src); err != nil {
log.Fatal(err)
}
}
func main() {
conn, err := net.Dial("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
go mustCopy(os.Stdout, conn)
mustCopy(conn, os.Stdin)
}