一、背景
经历了多个版本,基础内容在前面,可以使用之前的基础环境:
v1:
https://blog.csdn.net/wtt234/article/details/132139454v2:
https://blog.csdn.net/wtt234/article/details/132144907v3:
https://blog.csdn.net/wtt234/article/details/132148572v4:
https://blog.csdn.net/wtt234/article/details/132169338v5:
https://blog.csdn.net/wtt234/article/details/132169651
二、代码
2.1main.go
package main
func main() {
server := NewServer("127.0.0.1", 8888)
server.Start()
}
2.2server.go
package main
import (
"fmt"
"io"
"net"
"sync"
)
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的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) BroadCast(user *User, msg string) {
sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg
this.Message <- sendMsg
}
func (this *Server) Handler(conn net.Conn) {
//...当前链接的业务
//fmt.Println("链接建立成功")
user := NewUser(conn, this)
user.Online()
//接受客户端发送的消息
go func() {
buf := make([]byte, 4096)
for {
n, err := conn.Read(buf)
if n == 0 {
user.Offline()
return
}
if err != nil && err != io.EOF {
fmt.Println("Conn Read err:", err)
return
}
//提取用户的消息(去除'\n')
msg := string(buf[:n-1])
//用户针对msg进行消息处理
user.DoMessage(msg)
}
}()
//当前handler阻塞
select {}
}
//启动服务器的接口
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
}
//close listen socket
defer listener.Close()
//启动监听Message的goroutine
go this.ListenMessager()
for {
//accept
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener accept err:", err)
continue
}
//do handler
go this.Handler(conn)
}
}
2.3uer.go
package main
import (
"net"
"strings"
)
type User struct {
Name string
Addr string
C chan string
conn net.Conn
server *Server
}
//创建一个用户的API
func NewUser(conn net.Conn, server *Server) *User {
userAddr := conn.RemoteAddr().String()
user := &User{
Name: userAddr,
Addr: userAddr,
C: make(chan string),
conn: conn,
server: server,
}
//启动监听当前user channel消息的goroutine
go user.ListenMessage()
return user
}
//用户的上线业务
func (this *User) Online() {
//用户上线,将用户加入到onlineMap中
this.server.mapLock.Lock()
this.server.OnlineMap[this.Name] = this
this.server.mapLock.Unlock()
//广播当前用户上线消息
this.server.BroadCast(this, "已上线")
}
//用户的下线业务
func (this *User) Offline() {
//用户下线,将用户从onlineMap中删除
this.server.mapLock.Lock()
delete(this.server.OnlineMap, this.Name)
this.server.mapLock.Unlock()
//广播当前用户上线消息
this.server.BroadCast(this, "下线")
}
//给当前User对应的客户端发送消息
func (this *User) SendMsg(msg string) {
this.conn.Write([]byte(msg))
}
//用户处理消息的业务
func (this *User) DoMessage(msg string) {
if msg == "who" {
//查询当前在线用户都有哪些
this.server.mapLock.Lock()
for _, user := range this.server.OnlineMap {
onlineMsg := "[" + user.Addr + "]" + user.Name + ":" + "在线...\n"
this.SendMsg(onlineMsg)
}
this.server.mapLock.Unlock()
} else if len(msg) > 7 && msg[:7] == "rename|" {
//消息格式: rename|张三
newName := strings.Split(msg, "|")[1]
//判断name是否存在
_, ok := this.server.OnlineMap[newName]
if ok {
this.SendMsg("当前用户名被使用\n")
} else {
this.server.mapLock.Lock()
delete(this.server.OnlineMap, this.Name)
this.server.OnlineMap[newName] = this
this.server.mapLock.Unlock()
this.Name = newName
this.SendMsg("您已经更新用户名:" + this.Name + "\n")
}
} else {
this.server.BroadCast(this, msg)
}
}
//监听当前User channel的 方法,一旦有消息,就直接发送给对端客户端
func (this *User) ListenMessage() {
for {
msg := <-this.C
this.conn.Write([]byte(msg + "\n"))
}
}
三、客户端测试
3.1服务端启动
go run main.go server.go user.go
3.2客户端运行试验内容
去另外一个客户端查看下自己的名字在在线里面是不是修改了
在修改另外一个客户端的名字,在查看效果