首页 > 其他分享 >即时通讯系统 -- V0.2用户上线及广播功能

即时通讯系统 -- V0.2用户上线及广播功能

时间:2023-03-06 16:03:54浏览次数:33  
标签:map string -- 用户 即时通讯 user 连接 V0.2 User

V0.1实现了server的基础构建,并在main.go中测试了连接成功
V0.2要实现用户上线功能,并且某用户上线时所有在线用户都会收到该用户的上线信息,即蓝线功能
image

  1. 实现用户上线功能,先定义一个用户结构体
type User struct {
	Name string
	Addr string
	C    chan string
	conn net.Conn
}

Name为用户名(为了方便Name与Addr一致)
Addr为ip地址
C为用户的chanel
conn为该用户与服务器tcp连接

  1. 创建一个用户的API
// 创建一个用户的API
func NewUser(conn net.Conn) *User {
	userAddr := conn.RemoteAddr().String()

	user := &User{
		Name: userAddr,
		Addr: userAddr,
		C:    make(chan string),
		conn: conn,
	}

	//启动监听当前user channel消息的 goruntine
	go user.ListenMessage()

	return user
}
  1. 监听当前 user 的 channel,一旦有消息直接发给对端客户端
func (this *User) ListenMessage() {
	for {
		msg := <-this.C
		//net.Conn.Write()向网络连接里写数据,参数是字节切片,因此要转换一下
		this.conn.Write([]byte(msg + "\n"))
	}
}
  1. 完善 server 结构体
    1. 要维护当前在线用户,需要一个 map
    2. 可能多个 goruntine 访问该 map,需要加读写互斥锁
    3. server 也需要一个 channel 来存储广播消息
type Server struct {
	Ip   string
	Port int

	//在线用户列表
	OnlineMap map[string]*User
	mapLock   sync.RWMutex

	//消息广播的channel
	Message chan string
}
  1. server 监听 Message 广播消息的 goruntine,一旦有消息就发送给全部在线 uesr
// 广播消息的方法
func (this *Server) BroadCast(user *User, msg string) {
	sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg
	this.Message <- sendMsg
}

// 监听Message广播消息的goroutine,一旦有消息就发给全部的在线user
func (this *Server) ListenMessager() {
	for {
		msg := <-this.Message

		//将msg发给全部在线user
		this.mapLock.Lock()
		for _, cli := range this.OnlineMap {
			cli.C <- msg
		}
		this.mapLock.Unlock()
	}
}
func (this *Server) Handler(conn net.Conn) {
	//当前连接的业务
	//fmt.Println("连接建立成功")
	user := NewUser(conn)

	//用户上线,将用户加入OnlineMap中
	this.mapLock.Lock()
	this.OnlineMap[user.Name] = user
	this.mapLock.Unlock()

	//广播当前用户上线信息
	this.BroadCast(user, "已上线")

	//当前handler阻塞
	select {}
}

总结

  1. 在Handler()中,select{}语句的作用是让当前的Handler方法永久阻塞,以保持该连接的持续性。如果没有这个select{}语句,当一个连接被处理完后,Handler方法会立即返回,从而导致连接被关闭。
    由于该Server使用goroutine并发处理多个客户端连接,每个连接都会在单独的goroutine中运行。在每个连接的Handler方法中,为了保持连接持续,需要使用某种方法防止连接被关闭,以便在接收到来自客户端的数据时能够继续处理。select{}语句是一种常见的阻塞方法,它可以防止当前goroutine返回并保持连接持续。
    这种阻塞方法并不是最佳的选择,因为它会浪费CPU资源。更好的方式是使用通道或定时器,以便在接收到数据或超时时解除阻塞。

  2. 要加读写锁mapLock,是因为OnlineMap这个map可能会被多个goroutine同时访问,这样会导致数据不一致或者竞态条件。为了保证数据的安全性和一致性,需要用sync.RWMutex这个类型的锁来控制对map的读写操作。sync.RWMutex是一个读写互斥锁,它允许多个goroutine同时读取map,但是只允许一个goroutine写入map,并且读写是互斥的

标签:map,string,--,用户,即时通讯,user,连接,V0.2,User
From: https://www.cnblogs.com/hzy717zsy/p/17182138.html

相关文章