美文网首页
34.Go语言·第三个项目《用户通讯系统》

34.Go语言·第三个项目《用户通讯系统》

作者: 一枼落知天下 | 来源:发表于2019-06-18 09:46 被阅读0次

特别提醒:Bug一大堆:先略过....后期在来慢慢修,我先会,然后慢慢优化...希望有大佬指点迷津,谢谢

ReadME.md

#程序框架图:
    一、服务器端程序框架图[思路]:
        main.go
            1.监听
            2.等待客户端的连接
            3.初始化的任务(一些准备工作)
        processor.go
            1.管家,根据客户端的求情,请求分发。分配
            2.任务调度
            3.调用相应的处理器,完成任务

            UserProcess.go
                1.处理跟用户相关的请求
                2.登录、注册、注销、用户信息、列表
            SmsProcess.go
                1.处理 短消息
                2.群聊、点对点
            IMProcess.go

            FileProcess.go...
        
    二、core核心公用的包
        Common.go
        User.go
        Utils.go
        1.工具类 :提供常用的方法和函数

    三、客户端程序框架图[思路]:

程序框架

程序框架

效果图:


AppIM/core--package core

Common.go

package core

// 定义消息类型常量
const (
    LoginReqMsgType         = "LoginReqMsg" // 登录客户端请求消息
    LoginResMsgType         = "LoginResMsg" // 登录服务端应答消息
    RegReqMsgType           = "RegReqMsg"   // 注册客户端请求消息
    RegResMsgType           = "RegResMsg"   // 注册服务端应答消息
    NotifyUserStatusMsgType = "NotifyUserStatusMsg"
    SmsMsgType              = "SmsMsg"
)

// 用户状态常量
// 
const (
    Online = iota
    Offline
    Busy
    Gaming
    Diamond
)

type Message struct {
    Type string `json:"type"` //消息类型
    Data string `json:"data"` //消息数据
}

type Repsonse struct {
    Code  int    `json:"code"`  //返回状态码
    Error string `json:"error"` //返回错误信息
}

// 登录请求消息
type LoginReqMsg struct {
    User User `json:"user"`
}

// 登录服务器应答消息
type LoginResMsg struct {
    Repsonse
    OnlineUsers map[int]*User  `json:"onlineUsers"`
}

// 注册请求消息
type RegReqMsg struct {
    User User `json:"user"`
}

// 注册服务器应答消息
type RegResMsg struct {
    Repsonse
}

// 配合服务器端推送通知用户状态变化的消息
type NotifyUserStatusMsg struct {
    User   *User `json:"user"`
}

// 消息结构体
type SmsMsg struct {
    User
    Content string `json:"content"`
}

Error.go

package core

import (
    "errors"
)

var (
    ERROR_USER_NOTEXISTS = errors.New("用户名不存在,请先注册!")
    ERROR_USER_EXISTS = errors.New("用户名已经被注册,请登录!")
    ERROR_USER_PWD = errors.New("用户名或密码错误!")
)

Redis.go

package core

import (
    "time"
    "github.com/garyburd/redigo/redis"
)

var content string = `
————————————————Go语言·redis连接池————————————————————
`

var (
    Pool *redis.Pool
)


func InitPool(address string,maxIdle, maxActive int,idleTimeout time.Duration,
    password string) {
    Pool = &redis.Pool{
        MaxIdle:     maxIdle,   //最大空闲链接数
        MaxActive:   maxActive,    //表示和数据库的最大连接数,0表示没有限制
        IdleTimeout: idleTimeout,  //最大空闲时间
        Dial: func() (redis.Conn, error) {
            // 初始化连接
            return redis.Dial("tcp", address, redis.DialPassword(password))
        },
    }
}

User.go

package core


type User struct {
    UserID   int    `json:"userID"`   //用户id
    UserPwd  string `json:"userPwd"`  //密码
    UserName string `json:"userName"` //用户名
    UserStatus  int  `json:"userStatus"` //用户状态
}

Utils.go

package core

import (
    "fmt"
    "net"
    "errors"
    "encoding/binary"
    "encoding/json"
)

// 数据传输
type Transfer struct {
    // 连接
    Conn net.Conn
    // 缓冲
    buffer [8096]byte
}


/**
 * [ReadPkg 读取数据包]
 * @author Jhou Shuai
 * @datetime 2019-06-15T10:27:55+0800
 */
func  (this *Transfer)ReadPkg() (msg Message,err error) {
    // fmt.Printf("等待对方发送数据,对方ip=%v \n",this.Conn.RemoteAddr().String())
    _,err = this.Conn.Read(this.buffer[:4])
    if err != nil {
        // err = errors.New("服务器端,读取Pkg长度失败!")
        return
    }

    var pkgLen uint32 

    pkgLen = binary.BigEndian.Uint32(this.buffer[:4])

    // 根据pkgLen读取消息内容
    n,err := this.Conn.Read(this.buffer[:pkgLen])
    if n != int(pkgLen) || err != nil {
        // err = errors.New("服务器端,读取Pkg消息内容失败!")
        return 
    }

    //反序列化-》core.Message
    err = json.Unmarshal(this.buffer[:pkgLen], &msg)
    if err != nil {
        err = errors.New("数据包反序列化失败!")
        return 
    }
    return 
}

/**
 * [WritePkg 发送数据包]
 * @author Jhou Shuai
 * @datetime 2019-06-15T10:27:32+0800
 */
func  (this *Transfer)WritePkg(pkgDta []byte) (err error) {
    // 先获取到pkgDta的长度
    // 发送一个数据包pkgDta长度给对方
    // 数据包长度pkgDta---》转成一个表示长度的byte切片
    var pkgLen uint32 

    pkgLen = uint32(len(pkgDta))
    binary.BigEndian.PutUint32(this.buffer[0:4],pkgLen)

    // 发送包长度
    n,err := this.Conn.Write(this.buffer[:4])
    if n!=4 ||err != nil {
        fmt.Println("数据包长度,发送失败!",err)
        return 
    }

    // 发送数据包pkgDta本身
    n,err = this.Conn.Write(pkgDta)
    if n != int(pkgLen) || err != nil {
        fmt.Println("发包失败!",err)
        return 
    }
    return 
}

AppIM/client/----客户端

AppIM/client/main.go

// 系统程序入口
package main

import (
    App "AppIM/client/entry"
)

func main() {
    App.InitApp().Go()
}

AppIM/client/entry/Processor.go

package entry

import (
    "fmt"
    "AppIM/client/process"
)

var menu string = `
-----------欢迎加入多人聊天系统~--------
    1.登录
    2.注册
    3.退出
请选择(1-3)>>> `

type processor struct {
    item int   //菜单编号
    loop bool  //表示是否循环显示菜单
    ok string  //[Y/N]
    user *process.UserProcess
}


/**
 * 初始胡应用
 */
func InitApp() *processor {
    return &processor{
        item:0,
        loop:false,
        ok:"y",
        user:process.InitUserProcess(),
    }
}


/**
 * [Go 启动应用]
 * @author Jhou Shuai
 * @datetime 2019-06-09T13:44:50+0800
 */
func (this *processor) Go() {
    for {
        //显示菜单
        fmt.Print(menu)
        fmt.Scanln(&this.item)
        switch this.item {
            case 1:  //1.登录
                this.login()
            case 2:  //2.注册
                this.register()
            case 3:  //5.退出
                this.exit()
            default: //其他非法输入
                this.otherService()
        }
        if this.loop {
            break
        }
    }
}


/**
 * [otherService 其他服务,暂未开放...]
 * @author Jhou Shuai
 * @datetime 2019-06-09T13:27:14+0800
 */
func (this *processor) otherService() {
    fmt.Println("目前系统不支持此项服务,请重新输入!")
}


/**
 * 退出系统
 */
func (this *processor) exit() {
    for {
        fmt.Print("您确定要退出么?[y/n]")
        fmt.Scanln(&this.ok)
        if this.ok == "Y" || this.ok == "y" {
            fmt.Println()
            fmt.Println("[成功退出]多人聊天系统!**********")
            this.loop = true
            break;
        }else if this.ok == "n" || this.ok == "N"{
            this.loop = false
            break;
        }
    }
}


/**
 * [login 登录]
 * @author Jhou Shuai
 * @datetime 2019-06-14T16:29:16+0800
 */
func (this *processor) login() {
    var (
        uid int
        pwd string
    )
    fmt.Print("请输入用户ID>>>")
    fmt.Scanln(&uid)

    fmt.Print("请输入密码>>>")
    fmt.Scanln(&pwd)
    this.user.Login(uid, pwd)
}

/**
 * [register 注册]
 * @author Jhou Shuai
 * @datetime 2019-06-14T16:29:16+0800
 */
func (this *processor) register() {
    var (
        userID int
        userPwd string
        userName string

    )
    fmt.Print("请输入注册ID>>>")
    fmt.Scanln(&userID)

    fmt.Print("请输入用户名>>>")
    fmt.Scanln(&userName)

    fmt.Print("请输入密码>>>")
    fmt.Scanln(&userPwd)

    this.user.Register(userID,userPwd,userName)
}

AppIM/client/model/UserModel.go

package model

import (
    "net"
    "AppIM/core"
)


type CurrUser struct {
    *core.User
    Conn net.Conn
}

AppIM/client/process

ServerProcess.go

package process

import (
    "fmt"
    "os"
    "net"
    "AppIM/core"
    "encoding/json"
)

var menu string = `
-----------恭喜<NO.%.4d号[%v]>登录成功,您可以进行如下操作--------
    1.在线列表
    2.发送消息
    3.信息列表
    4.查看留言
    5.退出系统
请选择(1-5)>>> `


type ServerProcess struct {
    item int   //菜单编号
    loop bool  //表示是否循环显示菜单
    ok string  //[Y/N]
    Conn net.Conn
}



/**
 * 初始胡应用
 */
func InitApp(conn net.Conn) *ServerProcess {
    return &ServerProcess{
        item:0,
        loop:false,
        ok:"y",
        Conn:conn,
    }
}


/**
 * [LoginSucc 登录成功]
 * @author Jhou Shuai
 * @datetime 2019-06-09T13:44:50+0800
 */
func (this *ServerProcess) LoginSucc(user *core.User) {
    for {
        //显示菜单
        var content string
        var smsProcess = &SmsProcess{}
        fmt.Printf(menu,user.UserID,user.UserName)
        fmt.Scanln(&this.item)
        switch this.item {
            case 1:  //1.在线列表
                ShowAllOnlineUser()
            case 2:  //2.发送消息
                fmt.Println("请输入>>>")
                fmt.Scanln(&content)
                smsProcess.SendGroupMsg(content)
            case 3:  //3.信息列表
                fmt.Println("3.信息列表")
            case 4:  //4.查看留言
                fmt.Println("4.查看留言")
            case 5:  //5.退出系统
                fmt.Println("\n[成功退出]多人聊天系统!**********")
                os.Exit(0)
            default:
                fmt.Println("目前系统不支持此项服务,请重新输入!")
        }
        if this.loop {
            break
        }
    }
}


// 启动一个协程,保持和服务端的通讯,如果服务器有数据推送
// 给客户端,则可以接收并显示在客户端的终端
func (this *ServerProcess) HandleServerProcess() {
    transfer := &core.Transfer{
        Conn:this.Conn,
    }
    for {
        msg,err := transfer.ReadPkg()
        if err != nil {
            fmt.Println("\n服务器连接中断...")
            return 
        }
        // 如果读取到消息,进行下一步处理逻辑
        fmt.Println("\n新消息来啦!请注意查收...")
        switch msg.Type {
            case core.NotifyUserStatusMsgType:
                // user := &core.User{}
                // 取出上线通知
                // 把上线用户的信息,保存到客户端的map中
                // userMrg.AddOnlineUser()
                var notifyUserStatusMsg core.NotifyUserStatusMsg
                err = json.Unmarshal([]byte(msg.Data), &notifyUserStatusMsg)
                if err != nil {
                    return 
                }
                AddOnlineUser(notifyUserStatusMsg.User)
            case core.SmsMsgType:
                // 处理消息
                showGroupMsg(msg)
            default:
                fmt.Println("暂时无法处理...",msg)
        }
    }
}

SmsMrg.go

package process

import (
    "fmt"
    "AppIM/core"
    "encoding/json"
)


func showGroupMsg(msg core.Message) {
    var smsMsg  core.SmsMsg
    err := json.Unmarshal([]byte(msg.Data), &smsMsg)
    if err != nil {
        fmt.Println("Client反序列化群消息SmsMsg失败!")
        return 
    }
    fmt.Println("****************************************")
    fmt.Printf("<NO.%.4d号 [%v]>:\n",smsMsg.UserID,smsMsg.UserName)
    fmt.Println(smsMsg.Content)
}

SmsProcess.go

package process

import (
    "fmt"
    "encoding/json"
    "AppIM/core"
)

type SmsProcess struct {}


// 发送群聊的消息
func (this *SmsProcess) SendGroupMsg(content string) (err error){
    // 1.创建一个msg
    var msg core.Message
    msg.Type = core.SmsMsgType

    var smsMsg core.SmsMsg
    smsMsg.Content = content
    smsMsg.User    = *currUser.User
    fmt.Println(smsMsg)
    fmt.Println(currUser.Conn)
    data,err := json.Marshal(smsMsg)
    if err != nil {
        fmt.Println("您发送的消息,序列化失败!",err)
        return 
    }

    msg.Data = string(data)
    msgData,err:= json.Marshal(msg)
    if err != nil {
        fmt.Println("您发送的消息,序列化失败!",err)
        return 
    }
    // msgData,就是我们要发送的消息
    transfer := &core.Transfer{
        Conn: currUser.Conn,
    }
    err = transfer.WritePkg(msgData)
    if err != nil {
        fmt.Println("您发送的消息,发送失败!", err)
        return
    }
    return 
}


UserMrg.go

package process

import (
    "fmt"
    "AppIM/core"
    "AppIM/client/model"
)

// 客户端维护的在线列表
var onlineUsers map[int]*core.User = make(map[int]*core.User,10)
// 当前用户信息 全局变量
var currUser model.CurrUser

// 添加在线用户
func AddOnlineUser(user *core.User) {
    _,ok := onlineUsers[user.UserID]
    if !ok {
        onlineUsers[user.UserID] = user
    }
    onlineUsers[user.UserID].UserStatus = core.Online
    ShowAllOnlineUser()
}

// 删除
func DelOnlineUser(userID int) {
    delete(onlineUsers, userID)
}



func ShowAllOnlineUser() {
    fmt.Println("****************************************")
    fmt.Println("当前在线用户列表如下:")
    if len(onlineUsers)>0{
        for id,userItem := range onlineUsers{
            fmt.Printf("<NO.%.4d号 [%v]>~~~在线...\n",id,userItem.UserName)
        }
    }else{
        fmt.Println("暂无...小伙伴儿都在休息...")
    }
    fmt.Println("****************************************")
}

UserProcess.go

package process

import (
    "AppIM/core"
    "encoding/json"
    "fmt"
    "net"
)

type UserProcess struct{}

func InitUserProcess() *UserProcess {
    return &UserProcess{}
}

/**
 * [login 登录]
 * @author Jhou Shuai
 * @datetime 2019-06-14T16:29:16+0800
 */
func (this *UserProcess) Login(userID int, userPwd string) (err error) {
    // 1.连接到服务器端
    conn, err := net.Dial("tcp", "127.0.0.1:8889")
    if err != nil {
        fmt.Println("网络连接失败咯!", err)
        return
    }
    // 延时关闭
    defer conn.Close()
    // 2.准备通过conn发送消息给服务器
    var msg core.Message
    msg.Type = core.LoginReqMsgType

    // 3.创建一个LoginReqMsg结构体
    var loginReqMsg core.LoginReqMsg
    loginReqMsg.User.UserID  = userID
    loginReqMsg.User.UserPwd = userPwd

    // 4.将loginReqMsg序列化
    data, err := json.Marshal(loginReqMsg)
    if err != nil {
        fmt.Println("loginReqMsg数据序列化失败!", err)
        return
    }
    // 5.将loginReqMsg序列化得到的data赋给msg.Data
    msg.Data = string(data)

    // 6.将msg进行序列化
    reqData, err := json.Marshal(msg)
    if err != nil {
        fmt.Println("ReqMsgData数据序列化失败!", err)
        return
    }
    // 7.reqData,就是我们要发送的消息
    transfer := &core.Transfer{
        Conn: conn,
    }
    err = transfer.WritePkg(reqData)
    if err != nil {
        fmt.Println("消息发送失败!", err)
        return
    }

    msg, err = transfer.ReadPkg()
    if err != nil {
        fmt.Println("服务器端,没有响应...")
        return
    }
    var loginResMsg core.LoginResMsg
    err = json.Unmarshal([]byte(msg.Data), &loginResMsg)
    if err != nil {
        return 
    }
    if loginResMsg.Code == 200 {
        user := &core.User{}
        for id,userItem := range loginResMsg.OnlineUsers{
            if id == userID {
                user = userItem
                continue
            }
            // 初始化在线列表
            onlineUsers[id] = userItem
        }
        fmt.Println("登录成功!")

        //当前用户信息 
        currUser.Conn = conn
        currUser.User = user
        fmt.Println("当前用户信息:",currUser)
        
        server := InitApp(conn)
        // 启动一个协程,保持和服务端的通讯,如果服务器有数据推送
        // 给客户端,则可以接收并显示在客户端的终端
        go server.HandleServerProcess()
        // 1.循环显示菜单:
        server.LoginSucc(user)
    } else {
        fmt.Println(loginResMsg.Error)
    }
    return
}

/**
 * [register 注册]
 * @author Jhou Shuai
 * @datetime 2019-06-14T16:29:16+0800
 */
func (this *UserProcess) Register(userID int,userPwd,userName string) (err error) {
    // 1.连接到服务器端
    conn, err := net.Dial("tcp", "127.0.0.1:8889")
    if err != nil {
        fmt.Println("网络连接失败咯!", err)
        return
    }
    // 延时关闭
    defer conn.Close()
    // 2.准备通过conn发送消息给服务器
    var msg core.Message
    msg.Type = core.RegReqMsgType

    // 3.创建一个RegReqMsg结构体
    var regReqMsg core.RegReqMsg
    regReqMsg.User.UserID   = userID
    regReqMsg.User.UserPwd  = userPwd
    regReqMsg.User.UserName = userName

    // 4.将regReqMsg序列化
    data, err := json.Marshal(regReqMsg)
    if err != nil {
        fmt.Println("regReqMsg数据序列化失败!", err)
        return
    }
    // 5.将regReqMsg序列化得到的data赋给msg.Data
    msg.Data = string(data)

    // 6.将msg进行序列化
    reqData, err := json.Marshal(msg)
    if err != nil {
        fmt.Println("ReqMsgData数据序列化失败!", err)
        return
    }
    // 7.reqData,就是我们要发送的消息
    transfer := &core.Transfer{
        Conn: conn,
    }
    err = transfer.WritePkg(reqData)
    if err != nil {
        fmt.Println("消息发送失败!", err)
        return
    }

    msg, err = transfer.ReadPkg()
    if err != nil {
        fmt.Println("服务器端,没有响应...")
        return
    }
    var regResMsg core.RegResMsg
    err = json.Unmarshal([]byte(msg.Data), &regResMsg)
    if regResMsg.Code == 200 {
        fmt.Println("注册成功,请重新登录!")
    } else {
        fmt.Println(regResMsg.Error)
    }
    return
}

IMProcess.go

package process


AppIM/server/----服务器端

AppIM/server/main/main.go

package main

import (
    App "AppIM/server/entry"
)


func main() {
    // 开始拍片
    App.InitIM().Action()
}

AppIM/server/entryProcessor.go

package entry

import (
    "AppIM/core"
    "AppIM/server/model"
    "AppIM/server/process"
    "fmt"
    "io"
    "net"
    "time"
)

type processor struct {
    Conn net.Conn
}

func InitIM() *processor {
    initApp()
    return &processor{}
}

// 初始化
func initApp() {
    var (
        address     = "127.0.0.1:6379"
        maxIdle     = 16
        maxActive   = 0
        idleTimeout = 300 * time.Second
        password    = "Glen.Jhou@tutengdai.TTD"
    )
    core.InitPool(address, maxIdle, maxActive, idleTimeout, password)

    model.UserHandel = model.NewUserModel(core.Pool)
}

func (this *processor) Action() {
    this.listen()
}

// 监听端口,建立连接,启动协程
func (this *processor) listen() {
    fmt.Println("服务器在8889端口监听....")
    listen, err := net.Listen("tcp", "0.0.0.0:8889")
    // 关闭
    defer listen.Close()

    if err != nil {
        fmt.Println("服务器8889端口监听失败:....", err)
        return
    }

    for {
        // fmt.Println("8889端口成功,等待客户端连接....")
        this.Conn, err = listen.Accept()
        if err != nil {
            fmt.Println("listen.Accept,Failed....", err)
        }
        // 一旦连接成功,则启动一个协程,和客户端保持通讯...
        go this.actGoroutine()
    }

    return
}

// 一旦连接成功,则启动一个协程
func (this *processor) actGoroutine() {
    // 关闭
    defer this.Conn.Close()

    err := this.handle()
    if err != nil {
        fmt.Println("和客户端的通讯协程错误:", err)
        return
    }
}

// 读取数据包,并分发数据,
func (this *processor) handle() (err error) {
    // 关闭
    defer this.Conn.Close()

    // 循环接收客户端发送的数据
    for {
        // 读取数据包
        transfer := &core.Transfer{
            Conn: this.Conn,
        }
        msg, err := transfer.ReadPkg()
        if err != nil {
            if err == io.EOF {
                fmt.Println("客户端断开连接,服务器也退出...")
                return err
            } else {
                fmt.Println("读取数据包,失败!", err)
                return err
            }
        }
        // 打印消息内容
        fmt.Println("客户端发送数据:", msg)
        // 根据客户端发送消息种类不同,决定调用哪个函数来处理
        err = this.serverProcessMsg(&msg)
        if err != nil {
            return err
        }
    }
    return
}

// 编写一个ServerProcessMsg函数
// 功能:根据客户端发送消息种类不同,决定分配不同任务,调用相应的程序
func (this *processor) serverProcessMsg(msg *core.Message) (err error) {
    switch msg.Type {
    case core.LoginReqMsgType:
        // 处理登录
        user := &process.UserProcess{Conn: this.Conn}
        err = user.ServerProcessLogin(msg)
    case core.RegReqMsgType:
        //  处理注册
        user := &process.UserProcess{Conn: this.Conn}
        err = user.ServerProcessRegister(msg)
    case core.SmsMsgType:
        // 处理消息
        smsProcess := &process.SmsProcess{}
        smsProcess.SendGroupMsg(msg)
    default:
        fmt.Println("消息类型不存在,无法处理!")
    }
    return
}

AppIM/server/model/UserModel.go

package model

import (
    "fmt"
    "AppIM/core"
    "encoding/json"
    "github.com/garyburd/redigo/redis"
)

var (
    UserHandel *UserModel
    key  = "IM:users"
)

type UserModel struct {
    pool *redis.Pool 
}

// 使用工厂模式,创建一个UserModel的实例
func NewUserModel(pool *redis.Pool) *UserModel {
    return &UserModel{
        pool:pool,
    }
}

// 根据用户id,返回一个User实例 和err
func (this *UserModel) getUserById(redisConn redis.Conn,userID int) (user *core.User,err error) {

    // 根据id,去redis查询该用户信息
    res ,err  := redis.String(redisConn.Do("HGet",key,userID))
    if err != nil {
        // 表示没有找到用户信息
        if err == redis.ErrNil {
            err = core.ERROR_USER_NOTEXISTS
        }
        return 
    }
    
    user = &core.User{}

    err = json.Unmarshal([]byte(res), user)
    if err != nil {
        fmt.Println("Redis用户数据反序列化失败:",err)
        return 
    }
    return 
}


// 登录
func (this *UserModel) Login(userID int,userPwd string) (user *core.User,err error){
    // 获取redis连接
    redisConn := this.pool.Get()
    defer redisConn.Close()
    // 获取用户信息
    user ,err = this.getUserById(redisConn, userID)
    if err != nil {
        return 
    }

    // 验证用户的密码
    if user.UserPwd != userPwd {
        err = core.ERROR_USER_PWD
        return 
    }
    user.UserStatus = core.Online
    user.UserPwd = "(*^__^*) 嘻嘻"
    return 
}


func (this *UserModel) Register(user *core.User)(err error) {
    // 获取redis连接
    redisConn := this.pool.Get()
    defer redisConn.Close()
    
    _,err = this.getUserById(redisConn, user.UserID)
    if err == nil {
        // 该用户已经存在咯
        err = core.ERROR_USER_EXISTS
        return 
    }

    data ,err := json.Marshal(user)
    if err != nil {
        return 
    }

    _,err = redisConn.Do("HSet",key,user.UserID,string(data))
    if err != nil {
        return 
    }

    return 
}

AppIM/server/process

SmsProcess.go

package process

import (
    "fmt"
    "net"
    "AppIM/core"
    "encoding/json"
)

type SmsProcess struct {}


// 转发群消息
func (this *SmsProcess) SendGroupMsg(msg *core.Message) {
    // 1.先从msg中取出msg.Data,并直接反序列化成SmsMsg消息
    var smsMsg  core.SmsMsg
    err := json.Unmarshal([]byte(msg.Data), &smsMsg)
    if err != nil {
        fmt.Println("Server反序列化群消息SmsMsg失败!")
        return 
    }

    msgDta,err := json.Marshal(msg)
    if err != nil {
        fmt.Println("Server反序列化群消息msg失败!")
        return 
    }

    // 遍历服务器端  onlineUsers map[int]*UserProcess
    for id,userProcess := range userMrg.onlineUsers {
        if id == smsMsg.UserID {
            continue
        }
        this.sendMsgToOnlineUser(userProcess.Conn, msgDta)
    }
}


func (this *SmsProcess) sendMsgToOnlineUser(conn net.Conn,data []byte) {
    // 转发消息
    transfer := &core.Transfer{
        Conn:conn,
    }
    err := transfer.WritePkg(data)
    if err != nil {
        fmt.Println("~~~转发消息失败!")
        return 
    }
}

UserMrg.go

package process

import (
    "fmt"
)

var (
    userMrg *UserMrg
)

type UserMrg struct {
    onlineUsers map[int]*UserProcess
}

// 初始化
func init(){
    userMrg = &UserMrg{
        onlineUsers : make(map[int]*UserProcess,1024),
    }
}

// 添加在线用户
func (this *UserMrg) AddOnlineUser(user *UserProcess) {
    this.onlineUsers[user.UserID] = user
}

// 删除
func (this *UserMrg) DelOnlineUser(userID int) {
    delete(this.onlineUsers, userID)
}


// 返回所有当前在线的用户
func (this *UserMrg) GetAllOnlineUser() map[int]*UserProcess {
    return this.onlineUsers
}


// 根据id返回对应的用户
func (this *UserMrg) GetOnlineUserByID(userID int) (user *UserProcess,err error) {
    user,ok := this.onlineUsers[userID]
    if !ok {
        // 查找的用户当前不在线
        err = fmt.Errorf("用户NO.%.4d当前不在线!", userID)
        return 
    }
    return 
}

UserProcess.go

package process

import (
    "fmt"
    "net"
    "AppIM/core"
    "encoding/json"
    "AppIM/server/model"
)

type UserProcess struct {
    // 连接
    Conn net.Conn
    // 增加一个字段,表示Conn是那个用户的
    UserID int
    User *core.User
}

// 上线通知
// 推送通知给其他在线用户,发上线数据包
// userID用户通知其他的用户,我上线咯
func (this *UserProcess) NotifyOthersOnlineUsers() {
    for id,userProcess := range userMrg.onlineUsers{
        if id == this.User.UserID {
            continue
        }
        // 开始发送通知
        userProcess.NotifyOtherUserMeOnline(this.User)
    }
}

// 上线通知
func (this *UserProcess) NotifyOtherUserMeOnline(user *core.User)(err error) {
    // 上线通知数据包和消息
    var msg core.Message
    msg.Type = core.NotifyUserStatusMsgType
    var notifyUserStatusMsg core.NotifyUserStatusMsg
    notifyUserStatusMsg.User   = user
    data,err := json.Marshal(notifyUserStatusMsg)
    if err != nil {
        fmt.Println("上线通知数据包序列化失败!",err)
        return 
    }

    // 将序列化的数据放到msg包中
    msg.Data = string(data)
    // 对msg序列化
    msgData,err := json.Marshal(msg)
    if err != nil {
        fmt.Println("上线通知消息包序列化失败!",err)
        return 
    }

    // 7.发包
    transfer := &core.Transfer{
        Conn:this.Conn,
    }
    err = transfer.WritePkg(msgData)
    if err != nil {
        fmt.Println("上线通知发送失败!",err)
        return 
    }
    return 
}



// 处理登录
func (this *UserProcess)ServerProcessLogin(msg *core.Message) (err error) {
    // 核心代码
    // 1.先从msg中取出msg.Data,并直接反序列化成loginReqMsg消息
    var loginMsg  core.LoginReqMsg
    err = json.Unmarshal([]byte(msg.Data), &loginMsg)
    if err != nil {
        fmt.Println("反序列化成loginReqMsg消息失败!")
        return 
    }

    // 2.先声明一个 发送客户端的消息
    var repsMsg core.Message
    repsMsg.Type = core.LoginResMsgType
    // 服务器应答消息 LoginResMeg
    var loginResMsg core.LoginResMsg

    // 服务端:redis数据验证
    // model.UserHandel.Login
    user,err := model.UserHandel.Login(loginMsg.User.UserID, loginMsg.User.UserPwd)
    
    // fmt.Println("Redis数据验证:",user)

    if err != nil {
        loginResMsg.Code  = 500
        loginResMsg.Error = err.Error()
    }else{
        loginResMsg.Code  = 200
        loginResMsg.Error = "登录成功!"
        // 登录成功,把用户放入在线列表
        // 将登陆成功的,用户ID,记录
        this.UserID = loginMsg.User.UserID
        this.User   = user
        userMrg.AddOnlineUser(this)
        // 上线通知
        this.NotifyOthersOnlineUsers()
        loginResMsg.OnlineUsers = make(map[int]*core.User,10)
        for id,userProcess := range userMrg.onlineUsers{
            loginResMsg.OnlineUsers[id] = userProcess.User
        }
    }

    
    // 4.将loginResMsg序列化
    data,err := json.Marshal(loginResMsg)
    if err != nil {
        fmt.Println("loginResMsg数据序列化失败!",err)
        return 
    }
    // 5.将loginResMsg序列化得到的data赋给repsMsg.Data
    repsMsg.Data = string(data)

    // 6.将repsMsg进行序列化
    repsData,err := json.Marshal(repsMsg)
    if err != nil {
        fmt.Println("repsMsg数据序列化失败!",err)
        return 
    }
    // 7.发包
    transfer := &core.Transfer{
        Conn:this.Conn,
    }
    err = transfer.WritePkg(repsData)
    return 
}




// 处理登录
func (this *UserProcess)ServerProcessRegister(msg *core.Message) (err error) {
    // 核心代码
    // 1.先从msg中取出msg.Data,并直接反序列化成RegReqMsg消息
    var regMsg  core.RegReqMsg
    err = json.Unmarshal([]byte(msg.Data), &regMsg)
    if err != nil {
        fmt.Println("反序列化成注册RegReqMsg消息失败!")
        return 
    }

    // 2.先声明一个 发送客户端的消息
    var repsMsg core.Message
    repsMsg.Type = core.RegResMsgType
    // 服务器应答消息 regResMsg
    var regResMsg core.RegResMsg

    // 服务端注册
    // model.UserHandel.Login
    err = model.UserHandel.Register(&regMsg.User)
    
    fmt.Println("注册信息:",regMsg.User)
    if err != nil {
        regResMsg.Code  = 500
        regResMsg.Error = err.Error()
    }else{
        regResMsg.Code  = 200
        regResMsg.Error = "注册成功!"
    }


    // 4.将regResMsg序列化
    data,err := json.Marshal(regResMsg)
    if err != nil {
        fmt.Println("loginResMsg数据序列化失败!",err)
        return 
    }
    // 5.将loginResMsg序列化得到的data赋给repsMsg.Data
    repsMsg.Data = string(data)

    // 6.将repsMsg进行序列化
    repsData,err := json.Marshal(repsMsg)
    if err != nil {
        fmt.Println("repsMsg数据序列化失败!",err)
        return 
    }
    // 7.发包
    transfer := &core.Transfer{
        Conn:this.Conn,
    }
    err = transfer.WritePkg(repsData)
    return
}

IMProcess.go

package process

相关文章

网友评论

      本文标题:34.Go语言·第三个项目《用户通讯系统》

      本文链接:https://www.haomeiwen.com/subject/myhxqctx.html