一、聊天服务器结构图
尬聊服务器
二、效果演示
聊天服务器.gif
三、程序代码
// 广播器的编写
type client chan<- string // chan<- 只能发送
var (
entering = make(chan client) // 在线的客户端
leaving = make(chan client) // 离线的客户端
messages = make(chan string) // 所有接收到的客户消息
)
func broadcaster(){
clients := make(map[client]bool) // 所有连接的客户端
for {
select {
// 1, 消息广播给所有的客户端
case msg := <- messages:
for cli := range clients {
cli <- msg // 把消息传给每个客户端(TCP连接通道)
}
// 2, 注册用户
case cli := <-entering:
clients[cli] = true // 上线了
// 3, 注销用户
case cli := <-leaving:
delete(clients, cli) // 删除
close(cli) // 关闭这个通道
}
}
}
// 处理请求的编写
func handleChatConn(conn net.Conn){
// 对外发送客户消息的通道
clientCh := make(chan string)
go clientWriter(conn, clientCh)
// 获取客户端的地址,作为用户的区分,知道谁是谁的谁
who := conn.RemoteAddr().String() // T
// 每一个新用户(新的TCP连接),发送一条who信息给广播
clientCh <- "你是" + who
messages <- who + "上线!"
entering <- clientCh // 注册新用户(新建的TCP连接)
input := bufio.NewScanner(conn)
for input.Scan() {
// 阻塞读取信息,并将信息写入Socket缓冲区
messages <- who + ": " + input.Text()
}
// 对话框关闭后代表客户端退出
leaving <- clientCh // 注销用户
messages <- who + " 下线!"
conn.Close()
}
// 对客户端Tcp通道写入的编写
func clientWriter(conn net.Conn, ch <- chan string) {
for msg := range ch {
// 每当从客户端通道读取到信息,都转写入conn中
// 客户端TCP信息是由广播器写入的
fmt.Fprintln(conn, msg)
}
}
// 聊天服务的客户端编写
func ChatClient(){
conn, err := net.Dial("tcp", "localhost:8000")
if err!=nil {
log.Fatal(err)
}
done := make(chan struct{})
go func(){
io.Copy(os.Stdout, conn) // 读取客户端的信息到标准输出
log.Println("Done")
done <- struct{}{}
}()
mustCopy(conn, os.Stdin) // 读取标准输入到tcp通道
conn.Close()
<-done // 丢弃
}
func mustCopy(dst io.Writer, src io.Reader) {
_,err := io.Copy(dst, src)
if err != nil {
log.Fatal(err)
}
}
欢迎交流!
网友评论