多人聊天室【channel通信】

服务端

// *************思路********************
// 实现基本流程与乞丐版保持一致,只是在存储连接上修改为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)
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

koral chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值