GoLang 即时通讯系统(二)
用户消息广播功能
- 用户发送消息给服务器后,服务器需要将这条消息广播个当前的所以在线用户
用户业务封装处理
- 讲业务逻辑抽离到User类中,属于User类的上线,下线,发消息都封装到User类中
在线用户查询
名称修改
超时强踢
- 通过管道去触发定时器
私聊功能
- 解析客户端发送的字符串,根据规则进行拆分
- 从服务器中寻找相应的名称,进行单一发送即可
具体脚本
main
func main() {
s := NewServer("127.0.0.1", 8080)
s.Start()
}
server
package main
import (
"fmt"
"io"
"net"
"sync"
"time"
)
type Server struct {
Ip string
Port int
OnlineMap map[string]*User
onlineLock sync.RWMutex
MessageChan chan string
}
func NewServer(ip string, port int) *Server {
return &Server{
Ip: ip,
Port: port,
OnlineMap: make(map[string]*User),
MessageChan: make(chan string),
}
}
func (s *Server) handler(conn *net.TCPConn) {
fmt.Println("[Server] Connection Connect Success!!!", conn.RemoteAddr().String())
user := NewUser(conn, s)
user.Online()
buffer := make([]byte, 1024)
isLive := make(chan bool)
go func() {
for true {
count, err := conn.Read(buffer)
if count == 0 {
user.Offline()
return
}
if err != nil && err != io.EOF {
fmt.Println("[Error] Read Error: ", err)
return
}
isLive <- true
user.DoMessage(string(buffer[:count]))
}
}()
for true {
select {
case <-isLive:
case <-time.After(30 * time.Second):
user.SendMsg("由于长时间未发送消息,你被强制踢下线")
user.Offline()
return
}
}
}
func (s *Server) BroadCastMsg(user *User, msg string) {
sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg
s.MessageChan <- sendMsg
}
func (s *Server) ListenMsg() {
for true {
msg := <-s.MessageChan
s.onlineLock.Lock()
for _, user := range s.OnlineMap {
user.C <- msg
}
s.onlineLock.Unlock()
}
}
func (s *Server) Start() {
addr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", s.Ip, s.Port))
if err != nil {
fmt.Println(err)
return
}
listener, err := net.ListenTCP("tcp4", addr)
if err != nil {
fmt.Println("[Error] ListenTCP Error: ", err)
return
}
defer listener.Close()
go s.ListenMsg()
fmt.Println("[Server] Start Success!!!")
for true {
conn, err := listener.AcceptTCP()
if err != nil {
fmt.Println("[Error] AcceptTCP Error: ", err)
continue
}
go s.handler(conn)
}
}
user
package main
import (
"net"
"strings"
)
type User struct {
Name string
Addr string
C chan string
Conn *net.TCPConn
Server *Server
IsClose bool
}
func (u *User) SendMsg(msg string) {
u.Conn.Write([]byte(msg))
}
func (u *User) Online() {
u.Server.onlineLock.Lock()
u.Server.OnlineMap[u.Name] = u
u.Server.onlineLock.Unlock()
u.Server.BroadCastMsg(u, "已上线")
}
func (u *User) Offline() {
if u.IsClose {
return
}
u.IsClose = true
u.Server.onlineLock.Lock()
delete(u.Server.OnlineMap, u.Name)
u.Server.onlineLock.Unlock()
u.DoMessage("下线")
u.Conn.Close()
close(u.C)
}
func (u *User) DoMessage(msg string) {
if msg == "who" {
for _, user := range u.Server.OnlineMap {
u.SendMsg("[" + user.Addr + "]" + user.Name)
}
} else if len(msg) > 7 && msg[:7] == "rename|" {
newName := msg[7:]
_, ok := u.Server.OnlineMap[newName]
if ok {
u.SendMsg("名称重复")
} else {
u.Server.onlineLock.Lock()
delete(u.Server.OnlineMap, u.Name)
u.Server.OnlineMap[newName] = u
u.Server.onlineLock.Unlock()
u.Name = newName
u.SendMsg("修改名称成功")
}
} else if len(msg) > 3 && msg[:3] == "to|" {
strArr := strings.Split(msg, "|")
toName := strArr[1]
toMsg := strArr[2]
u.Server.onlineLock.RLock()
toUser, ok := u.Server.OnlineMap[toName]
u.Server.onlineLock.RUnlock()
if !ok {
u.SendMsg("查无此人[Name]:" + toName)
} else {
toUser.SendMsg("[" + u.Name + "]发送的私聊消息:" + toMsg)
}
} else {
u.Server.BroadCastMsg(u, msg)
}
}
func NewUser(conn *net.TCPConn, server *Server) *User {
userAddr := conn.RemoteAddr().String()
user := &User{
Name: userAddr,
Addr: userAddr,
C: make(chan string),
Conn: conn,
Server: server,
IsClose: false,
}
go user.ListenMsg()
return user
}
func (u *User) ListenMsg() {
for true {
msg, ok := <-u.C
if ok != true {
return
}
u.Conn.Write([]byte(msg))
}
}
test
package main
import (
"fmt"
"net"
)
func main() {
conn, err := net.Dial("tcp4", "127.0.0.1:8080")
if err != nil {
fmt.Println(err)
return
}
go func() {
buffer := make([]byte, 1024)
for true {
count, err := conn.Read(buffer)
if err != nil {
fmt.Println(err)
fmt.Println("断开连接")
return
}
fmt.Println(string(buffer[:count]))
}
}()
for true {
var str string
fmt.Scanln(&str)
_, err := conn.Write([]byte(str))
if err != nil {
fmt.Println(err)
}
}
}