服务端
// *************思路********************
// 实现基本流程与乞丐版保持一致,只是在存储连接上修改为channel通信、发送消息上也修改为channel通信
// 1.消息通道:message channel、entering channel、leaving channel
// 2.客户端连接通道:clients channel
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
)
type client chan<- string //send-only type for entering and leaving
var (
entering = make(chan client) //chan 类型的chan
leaving = make(chan client) //chan 类型的chan
messages = make(chan string) //广播给所有人
clients = make(map[client]bool)
)
func main() {
//1.监控端口
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
go broadCast() //消息路由
for {
//2.获取链接
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
}
//3.处理链接请求
go handlerConn(conn)
}
}
func handlerConn(conn net.Conn) {
//fmt.Println("conn")
ch := make(chan string) //每个连接用一个chan代替,里面进行每个连接的所有消息发送通道
go clientWriter(conn, ch) //给客户端发送消息
//1.给当前连接客户端返回他的登陆信息
connIP := conn.RemoteAddr().String()
loginInfo := "Your IP:" + connIP + "\n"
yourLoginInofForOther := connIP + " has arrived" + "\n"
fmt.Println(connIP, "login")
logOutInfo := connIP + " has left" + "\n"
ch <- loginInfo //给当前客户端发送消息
messages <- yourLoginInofForOther //这个放到ch的前面,是为了在注册之前发送只发送给其他客户端
entering <- ch
//2.监控当前客户端的输入信息,并且广播给其他客户端
input := bufio.NewScanner(conn)
for input.Scan() {
clientInput := connIP + ": " + input.Text() + "\n" //获取客户端连接的输入内容
messages <- clientInput
}
//3.监控当前客户端的登出消息,并广播给其他客户端
leaving <- ch
messages <- logOutInfo
conn.Close()
}
//消息路由器
func broadCast() {
for {
select {
case msg := <-messages:
for cli := range clients {
cli <- msg //将消息发送给每个客户端通信管道,这里配合的是 45行的gourountine
}
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 {
// _, err := io.WriteString(conn, msg)
// if err != nil {
// log.Fatal(err)
// }
fmt.Fprintln(conn, msg)
}
}
客户端
package main
import (
"io"
"log"
"net"
"os"
)
func main() {
conn, err := net.Dial("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
done := make(chan struct{})
go func() {
//获取服务端返回的数据
io.Copy(os.Stdout, conn) //阻塞
//fmt.Println("获取到数据")
log.Println("done")
done <- struct{}{}
}()
//fmt.Println("输入前")
mustCopy(conn, os.Stdin) //阻塞
//fmt.Println("输入后")
conn.Close()
<-done
}
//获取控制台输入的数据,传到服务端
func mustCopy(dst io.Writer, src io.Reader) {
if _, err := io.Copy(dst, src); err != nil {
log.Fatal(err)
}
}