GoLang 即时通讯系统(二)

GoLang 即时通讯系统(二)

用户消息广播功能

  • 用户发送消息给服务器后,服务器需要将这条消息广播个当前的所以在线用户
    在这里插入图片描述

用户业务封装处理

  • 讲业务逻辑抽离到User类中,属于User类的上线,下线,发消息都封装到User类中
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/020766b936114c0f8879d7a7586d3e2b.png

在这里插入图片描述

在线用户查询

在这里插入图片描述

名称修改

在这里插入图片描述

超时强踢

  • 通过管道去触发定时器
    在这里插入图片描述
    在这里插入图片描述

私聊功能

  • 解析客户端发送的字符串,根据规则进行拆分
  • 从服务器中寻找相应的名称,进行单一发送即可
    在这里插入图片描述

具体脚本

*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)
		}
	}
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值