go语言项目案例 | 青训营笔记
这是我参与「第五届青训营 」伴学笔记创作活动的第4天
简易而不简单,看着容易学着方便,但要写出真正用于生产环境的高效鲁棒性非常好的代码实则非常不容易,所以每一门语言都有自己的精髓所在,并不是看着简单就以为随便一学就可以应用的炉火纯青,非显性接口,Go程,channel通道,递归式结构体继承,看似简单,实则坑非常多,所以能用Go写出鲁棒性非常好的程序绝非易事,所以说Go简单实则是简约而不简单。
1.即时通信系统提纲
即时通信系统分为服务器端和客户端,服务器端设置用户在线列表,然后每个客户端上线后将上线消息加入在线列表中,然后服务器端监视列表,将上线用户广播通知给所有在线用户。其中还实现了用户广播功能和用户私信功能和用户查询在线列表和更改用户名功能。
2.构建基础server
首先创建一个server.go文件,然后创建server类型其中包含ip和port,然后创建一个创建server的接口,然后创建一个启动服务器接口监听用户上线,使用go协程加载处理业务逻辑,然后构建处理业务逻辑函数。
type Server struct {
Ip string
Port int
}
// 创建一个server的接口
func NewServer(ip string, port int) *Server {
server := &Server{
Ip: ip,
Port: port,
}
return server
}
// 启动服务器的接口
func (this *Server) Start() {
//socket listen
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", this.Ip, this.Port))
if err != nil {
fmt.Println("net.Listen err:", err)
return
}
go this.ListenMessager()
//accept
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener accept err:", err)
continue
}
//do handler
go this.Handler(conn)
}
//close listen socket
defer listener.Close()
}
创建main.go文件将ip地址和接口传入server对象中。
package main
func main() {
server := NewServer("127.0.0.1", 8888)
server.Start()
}
3.实现用户上线功能
当用户上线后,将其用户名和用户对象存入map数组中,然后其用户上线的消息发送给channel,其中使用一个goroutine进行管道message的监听,有消息传来就锁住,然后将message的上线的消息广播给大家,message中的消息发送给各个用户的channel中,然后通过服务器写的方式发送给客户端。这样用户也需要一个goroutine进行监听channel这样才能实现并发。 创建一个user.go文件,创建user类型,其中包含名称,地址,管道,链接,然后创建用户的接口,监听user对应的channel消息,server类型 新增OnlineMap和Message属性,在处理客户端上线的Handler创 建并添加⽤户,新增⼴播消息⽅法,新增监听⼴播消息channel⽅法,⽤⼀个goroutine单独监听Message。
type Server struct {
Ip string
Port int
//在线用户列表
OnLineMap map[string]*User
mapLock sync.RWMutex
//消息广播的channel
Message chan string
}
// 创建一个server的接口
func NewServer(ip string, port int) *Server {
server := &Server{
Ip: ip,
Port: port,
OnLineMap: make(map[string]*User),
Message: make(chan string),
}
return server
}
// 监听message广播消息channel的go程,一旦有消息就发送给全部的在线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) BroadCast(user *User, msg string) {
sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg
this.Message <- sendMsg
}