• GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库+协程)


     GO语言综合项目

      包含:

        1:GO语言基础知识

        2:TCP-Socket网络编程

        3:Redis数据库

      已实现:

        登录

          查看在线用户

          群聊

             私聊(未实现)

          历史消息(未实现)

        注册

        退出

     

     

    整体结构

    客户端client

         

     

        

     1 package main
     2 
     3 import (
     4     "ChatRoom/client/process"
     5     "fmt"
     6     "os"
     7 )
     8 
     9 var (
    10     userID   int
    11     userPsw  string
    12     userName string
    13 )
    14 
    15 func main() {
    16     var key int
    17     for {
    18         fmt.Println("------/ 多人互动聊天系统 /------")
    19         fmt.Printf("1		登录系统
    2		注册账户
    3		退出系统
    请输入(1-3)数字进行操作
    ")
    20         fmt.Scanln(&key)
    21         switch key {
    22         case 1:
    23             fmt.Println("登录聊天室")
    24             fmt.Println("请输入ID号")
    25             fmt.Scanln(&userID)
    26             fmt.Println("请输入密码")
    27             fmt.Scanln(&userPsw)
    28 
    29             //验证成功后
    30             /*mvs后,使用process下的UserProcess结构体实例
    31             调用绑定的Login登录方法
    32             */
    33             up := &process.UserProcess{}
    34             up.Login(userID, userPsw)
    35 
    36         case 2:
    37             //注册界面
    38             fmt.Println("请输入 ID号")
    39             fmt.Scanln(&userID)
    40             fmt.Println("请输入 密码")
    41             fmt.Scanln(&userPsw)
    42             fmt.Println("请输入 聊天昵称")
    43             fmt.Scanln(&userName)
    44             //调用UserProcess完成注册
    45             up := &process.UserProcess{}
    46             up.Register(userID, userPsw, userName)
    47         case 3:
    48             os.Exit(1)
    49         default:
    50             fmt.Println("输入有误,请重新输入(1-3)")
    51         }
    52     }
    53 }
    main.go

     1 package model
     2 
     3 import (
     4     "ChatRoom/conmon/message"
     5     "net"
     6 )
     7 
     8 /*
     9 CurrentUser 当前用户
    10 
    11 Conn    连接
    12 
    13 message.User    用户信息
    14 */
    15 type CurrentUser struct {
    16     Conn net.Conn
    17     message.User
    18 }
    currentUser.go

     1 package process
     2 
     3 import (
     4     "ChatRoom/client/utils"
     5     "ChatRoom/conmon/message"
     6     "encoding/json"
     7     "fmt"
     8     "net"
     9     "os"
    10 )
    11 
    12 //ShouMenu 显示登录后的界面菜单
    13 func ShouMenu() {
    14     for {
    15 
    16         var inputKey int
    17         var content string
    18 
    19         /*因为会经常用到SmsProcess,所以我们定义在Switch外部,减少创建实例化的次数*/
    20         SmsProcess := &SmsProcess{}
    21         fmt.Println(
    22             `
    23         1      显示在线用户
    24         2      聊天
    25         3      查看消息
    26         4      退出系统
    27 
    28         请输入(1-4)数字进行操作!
    29 
    30     `)
    31         fmt.Scanln(&inputKey)
    32         switch inputKey {
    33         case 1:
    34 
    35             /*调用显示在线用户的方法*/
    36             ShowOnLineUser()
    37 
    38         case 2:
    39 
    40             fmt.Println("请输入发送内容...")
    41             fmt.Scanln(&content)
    42             /*调用SmsProcess的发送消息方法*/
    43             SmsProcess.EnterMsg(content)
    44 
    45         case 3:
    46             fmt.Println("查看历史消息")
    47         case 4:
    48             fmt.Println("选择了退出系统...")
    49             os.Exit(1)
    50         default:
    51             fmt.Println("输入有误,请输入(1-4)")
    52         }
    53     }
    54 }
    55 
    56 //KeepConnection 保持客户端 与 服务端 之间的通讯,显示服务端推送的信息给客户端显示
    57 func KeepConnection(conn net.Conn) {
    58     //创建Tranfer实例,循环读取服务器发送的消息
    59     tf := &utils.Transfer{
    60         Conn: conn,
    61     }
    62     for {
    63         fmt.Println("")
    64         msg, err := tf.ReadPkg()
    65         if err != nil {
    66             fmt.Println("客户端读取服务器信息错误!	", err)
    67         }
    68         /*读取到消息,做进一步处理...*/
    69         switch msg.Type {
    70         case message.PushUserStatusMsgType:
    71             //有人上线了
    72 
    73             /*先实例化PushUserStatusMsg*/
    74             var pushUserStatusMsg message.PushUserStatusMsg
    75 
    76             /* 将 msg 反序列化*/
    77             json.Unmarshal([]byte(msg.Data), &pushUserStatusMsg)
    78 
    79             /* 将用户信息保存到 客户端维护的 map集合中 */
    80             UpdateUserStatus(&pushUserStatusMsg)
    81 
    82         case message.SmsMsgType:
    83             //有群发消息
    84 
    85             PinrtGroupMessage(&msg)
    86         default:
    87             fmt.Printf("返回的消息类型,暂时无法处理..
    ")
    88         }
    89 
    90     }
    91 }
    server.go
     1 package process
     2 
     3 import (
     4     "ChatRoom/conmon/message"
     5     "encoding/json"
     6     "fmt"
     7 )
     8 
     9 //PinrtGroupMessage 输出群发消息
    10 func PinrtGroupMessage(msg *message.Message) {
    11     /*反序列化msg.data*/
    12     var smsmsg message.SmsMsg
    13     err := json.Unmarshal([]byte(msg.Data), &smsmsg)
    14     if err != nil {
    15         fmt.Println("反序列化 msg.data 失败...")
    16         return
    17     }
    18 
    19     //显示信息
    20     talking := fmt.Sprintf("ID:%v	%v", smsmsg.UserID, smsmsg.Content)
    21     fmt.Println(talking)
    22     fmt.Println()
    23 }
    smsManger.go
     1 package process
     2 
     3 import (
     4     "ChatRoom/client/utils"
     5     "ChatRoom/conmon/message"
     6     "encoding/json"
     7     "fmt"
     8 )
     9 
    10 //SmsProcess 发送消息结构体
    11 type SmsProcess struct {
    12 }
    13 
    14 //EnterMsg 发送消息
    15 func (sp *SmsProcess) EnterMsg(content string) (err error) {
    16     /* 1 创建一个父类信息结构体实例化对象 */
    17     var msg message.Message
    18     msg.Type = message.SmsMsgType
    19 
    20     /* 2  创建一个 子类信息 结构体实例化对象 */
    21     var smsMsg message.SmsMsg
    22     smsMsg.Content = content
    23     smsMsg.UserID = cu.UserID
    24     smsMsg.UserStatus = cu.UserStatus
    25 
    26     /*3    将 smsMsg 序列化 */
    27     data, err := json.Marshal(smsMsg)
    28     if err != nil {
    29         fmt.Println("smsMsg 序列化失败!")
    30         return
    31     }
    32 
    33     /*4    将data 复制给 msg.Data */
    34     msg.Data = string(data)
    35 
    36     /*5    将 msg 序列化  */
    37     data, err = json.Marshal(msg)
    38     if err != nil {
    39         fmt.Println("msg 序列化 失败!")
    40         return
    41     }
    42 
    43     /*6    发送消息*/
    44     tf := &utils.Transfer{
    45         Conn: cu.Conn,
    46     }
    47 
    48     err = tf.WritePkg(data)
    49     if err != nil {
    50         fmt.Println("发送信息失败!")
    51         return
    52     }
    53     return
    54 }
    smsprocess.go
     1 package process
     2 
     3 import (
     4     "ChatRoom/client/model"
     5     "ChatRoom/conmon/message"
     6     "fmt"
     7 )
     8 
     9 //OnLineUsers 客户端维护的map
    10 var OnLineUsers map[int]*message.User = make(map[int]*message.User, 10)
    11 
    12 //CurrentUser 当前用户结构体
    13 var cu model.CurrentUser
    14 
    15 //ShowOnLineUser 在客户端显示所有的在线用户
    16 func ShowOnLineUser() {
    17     fmt.Println("↓当前在线用户:↓")
    18     for id := range OnLineUsers {
    19         fmt.Printf("id:	%v
    ", id)
    20     }
    21 }
    22 
    23 //UpdateUserStatus 处理服务器返回的 PushUserStatusMsg信息
    24 func UpdateUserStatus(pushUserStatusMsg *message.PushUserStatusMsg) {
    25 
    26     user, ok := OnLineUsers[pushUserStatusMsg.UserID]
    27     if !ok {
    28         user = &message.User{
    29             UserID: pushUserStatusMsg.UserID,
    30         }
    31     }
    32 
    33     user.UserStatus = pushUserStatusMsg.Status
    34     OnLineUsers[pushUserStatusMsg.UserID] = user
    35 
    36     /* 调用显示在线用户方法 */
    37     ShowOnLineUser()
    38 }
    userManger.go
      1 package process
      2 
      3 import (
      4     "ChatRoom/client/utils"
      5     "ChatRoom/conmon/message"
      6     "encoding/binary"
      7     "encoding/json"
      8     "fmt"
      9     "net"
     10     "os"
     11 )
     12 
     13 //UserProcess 用户处理器 结构体
     14 type UserProcess struct {
     15 }
     16 
     17 //Register 注册聊天账户
     18 func (up *UserProcess) Register(userID int, userPsw string, userName string) (err error) {
     19     //1 连接服务器
     20     conn, err := net.Dial("tcp", "127.0.0.1:9000")
     21     if err != nil {
     22         fmt.Println("连接服务器失败	", err)
     23         return
     24     }
     25 
     26     //延迟关闭数据库通道
     27     defer conn.Close()
     28 
     29     //2    连接服务端成功,准备发送数据
     30     var msg message.Message
     31     msg.Type = message.RegisterMsgType
     32 
     33     //3    创建 RegisterMsg 结构体
     34     var registerMsg message.RegisterMsg
     35     registerMsg.User.UserID = userID
     36     registerMsg.User.UserPsw = userPsw
     37     registerMsg.User.UserName = userName
     38 
     39     //4    将registerMsg 结构体序列化为json
     40     data, err := json.Marshal(registerMsg)
     41     if err != nil {
     42         fmt.Println("序列化失败! 	", err)
     43         return
     44     }
     45 
     46     //5    将data数据 赋值给 msg.Data
     47     msg.Data = string(data)
     48 
     49     //6    将msg 序列化 为json
     50     data, err = json.Marshal(msg)
     51     if err != nil {
     52         fmt.Println("序列化失败! 	", err)
     53         return
     54     }
     55 
     56     //7    创建一个Transfer实例
     57 
     58     tf := &utils.Transfer{
     59         Conn: conn,
     60     }
     61     //7.1    发送dada给服务器
     62     err = tf.WritePkg(data)
     63     if err != nil {
     64         fmt.Println("客户端:注册时发送数据错误! 	", err)
     65         os.Exit(0)
     66 
     67     }
     68     //7.2    读取消息
     69     msg, err = tf.ReadPkg()
     70     if err != nil {
     71         fmt.Println("ReadPkg(conn) 错误", err)
     72         os.Exit(0)
     73     }
     74 
     75     //7.3    反序列化
     76 
     77     //将m反序列化为 RegisterResMsg
     78     var registerResMsg message.RegisterResMsg
     79     err = json.Unmarshal([]byte(msg.Data), &registerResMsg)
     80 
     81     //判断返回值状态码
     82     if registerResMsg.Code == 200 {
     83         fmt.Println("注册成功,请登录!")
     84 
     85         /*登陆成功后...
     86         1    为客户端启动一个协程,该协程能确保客户端与服务器之间的通讯,
     87             若服务器有数据推送给客户端,则接受并显示在客户端终端上
     88         2    循环显示登陆成功后的菜单
     89         */
     90         go KeepConnection(conn)
     91 
     92         ShouMenu()
     93 
     94     } else {
     95         fmt.Println("注册失败,错误:")
     96         fmt.Println(registerResMsg.Error)
     97     }
     98     return
     99 
    100 }
    101 
    102 //Login 登录校验
    103 func (up *UserProcess) Login(userID int, userPsw string) (err error) {
    104 
    105     //1 连接服务器
    106     conn, err := net.Dial("tcp", "127.0.0.1:9000")
    107     if err != nil {
    108         fmt.Println("连接服务器失败	", err)
    109         return
    110     }
    111 
    112     //延迟关闭数据库通道
    113     defer conn.Close()
    114 
    115     //2    连接服务端成功,准备发送数据
    116     var msg message.Message
    117     msg.Type = message.LoginMsgType
    118 
    119     //3    创建 一个LoginMsg 结构体
    120     var loginMsg message.LoginMsg
    121     loginMsg.UserID = userID
    122     loginMsg.UserPsw = userPsw
    123 
    124     //4    将loginMsg 结构体序列化为json
    125     data, err := json.Marshal(loginMsg)
    126     if err != nil {
    127         fmt.Println("序列化失败! 	", err)
    128         return
    129     }
    130 
    131     //5    将data数据 赋值给 msg.Data
    132     msg.Data = string(data)
    133 
    134     //6    将msg 序列化 为json
    135     data2, err := json.Marshal(msg)
    136     if err != nil {
    137         fmt.Println("序列化失败! 	", err)
    138         return
    139     }
    140 
    141     //7 此时 data 就是我们客户端 → 服务端 发送的消息封装结构体
    142     var msgLen uint32 = uint32(len(data2))
    143     var buf [4]byte
    144     //将 信息长度msgLen 转换为 byte[]切片
    145     binary.BigEndian.PutUint32(buf[0:4], msgLen)
    146 
    147     //发送信息长度
    148     r, err := conn.Write(buf[:4])
    149     if r != 4 || err != nil {
    150         fmt.Println("消息长度发送失败!	", err)
    151         return
    152     }
    153     //fmt.Printf("发送长度成功,长度:%v	内容:%v", len(data2), string(data2))
    154 
    155     // 发送消息数据本身
    156     _, err = conn.Write(data2)
    157     if err != nil {
    158         fmt.Println("消息长度发送失败!	", err)
    159         return
    160     }
    161 
    162     //休眠10秒
    163     // time.Sleep(time.Second * 10)
    164     // fmt.Println("休眠10秒...")
    165     //这里处理服务器返回的消息
    166     //创建一个Transfer实例
    167 
    168     tf := &utils.Transfer{
    169         Conn: conn,
    170     }
    171     m, err := tf.ReadPkg()
    172     if err != nil {
    173         fmt.Println("ReadPkg(conn) 错误", err)
    174     }
    175 
    176     //将m反序列化为 LoginResMsg
    177     var loginresmsg message.LoginResMsg
    178     err = json.Unmarshal([]byte(m.Data), &loginresmsg)
    179 
    180     //判断返回值状态码
    181     if loginresmsg.Code == 200 {
    182 
    183         /* 初始化 CurrentUser */
    184         cu.Conn = conn
    185         cu.UserID = userID
    186         cu.UserStatus = message.OnLine
    187 
    188         fmt.Println("登陆成功,代码200")
    189         fmt.Println(loginresmsg.Error)
    190         /*显示当前在线用户列表,遍历loginresmsg.UserId*/
    191         for _, v := range loginresmsg.UsersID {
    192             //不显示自己
    193             if v == userID {
    194                 continue
    195             }
    196             fmt.Println("↓在线用户为:↓")
    197             fmt.Printf("ID:%v", v)
    198 
    199             /*完成对OnLineUser map的初始化*/
    200             user := &message.User{
    201                 UserID:     v,
    202                 UserStatus: message.OnLine,
    203             }
    204             OnLineUsers[v] = user
    205         }
    206         fmt.Printf("
    ")
    207         /*登陆成功后...
    208         1    为客户端启动一个协程,该协程能确保客户端与服务器之间的通讯,
    209             若服务器有数据推送给客户端,则接受并显示在客户端终端上
    210         2    循环显示登陆成功后的菜单
    211         */
    212         go KeepConnection(conn)
    213 
    214         ShouMenu()
    215 
    216     } else {
    217         fmt.Println("登陆失败,错误:")
    218         fmt.Println(loginresmsg.Error)
    219     }
    220     return
    221 }
    userprocess.go

     1 package utils
     2 
     3 import (
     4     "ChatRoom/conmon/message"
     5     "encoding/binary"
     6     "encoding/json"
     7     "fmt"
     8     "net"
     9 )
    10 
    11 //Transfer 将下列方法关联到结构体中
    12 type Transfer struct {
    13     //分析有哪些字段
    14     Conn net.Conn   //
    15     Buf  [8096]byte //缓冲区
    16 }
    17 
    18 //ReadPkg 读取封包数据
    19 func (tf *Transfer) ReadPkg() (msg message.Message, err error) {
    20 
    21     //buf := make([]byte, 8096)
    22 
    23     _, err = tf.Conn.Read(tf.Buf[:4])
    24 
    25     if err != nil {
    26         //自定义错误
    27         //err = errors.New("读取数据 头部 错误")
    28         return
    29     }
    30     //根据buf[:4] 转换成一个uint32
    31     var pkgLen uint32
    32     pkgLen = binary.BigEndian.Uint32(tf.Buf[0:4])
    33 
    34     //根据 pkgLen的长度读取内容 到 buf中
    35     n, err := tf.Conn.Read(tf.Buf[:pkgLen])
    36     if n != int(pkgLen) || err != nil {
    37         //自定义错误
    38         //err = errors.New("读取数据 内容 错误")
    39         return
    40     }
    41 
    42     //将pkglen 反序列化为 message.Massage  一定+&
    43     err = json.Unmarshal(tf.Buf[:pkgLen], &msg)
    44     if err != nil {
    45         fmt.Println("反序列化失败!	", err)
    46         return
    47     }
    48     return
    49 
    50 }
    51 
    52 //WritePkg 发送数据
    53 func (tf *Transfer) WritePkg(data []byte) (err error) {
    54     //1 发送信息长度
    55     var msgLen uint32 = uint32(len(data))
    56     //  var buf [4]byte
    57     //将 信息长度msgLen 转换为 byte[]切片
    58     binary.BigEndian.PutUint32(tf.Buf[0:4], msgLen)
    59 
    60     //发送信息长度
    61     r, err := tf.Conn.Write(tf.Buf[:4])
    62     if r != 4 || err != nil {
    63         fmt.Println("消息长度发送失败!	", err)
    64         return
    65     }
    66     //2    发送信息内容
    67     r, err = tf.Conn.Write(data)
    68     if r != int(msgLen) || err != nil {
    69         fmt.Println("消息长度发送失败!	", err)
    70         return
    71     }
    72     return
    73 }
    utils.go

    共用项common

      1 package message
      2 
      3 /*
      4 LoginMsgType 登录消息类型
      5 
      6 LoginResMsgType 登录返回消息类型
      7 
      8 RegisterMsgType 注册消息类型
      9 
     10 RegisterResMsgType 注册返回值信息类型
     11 
     12 PushUserStatusMsgType 服务器推送的用户状态信息类型
     13 
     14 SmsMsgType 发送的消息类型
     15 */
     16 const (
     17     LoginMsgType = "LoginMsg"
     18 
     19     LoginResMsgType = "LoginResMsg"
     20 
     21     RegisterMsgType = "Register"
     22 
     23     RegisterResMsgType = "RegisterResMsg"
     24 
     25     PushUserStatusMsgType = "PushUserStatusMsg"
     26 
     27     SmsMsgType = "SmsMsg"
     28 )
     29 
     30 /*用户状态常量
     31 
     32 OnLine    在线
     33 
     34 OffLine    离线
     35 
     36 Busy    繁忙
     37 */
     38 const (
     39     OnLine = iota
     40     OffLine
     41     Busy
     42 )
     43 
     44 //Message 消息结构体
     45 type Message struct {
     46     //Type 消息类型
     47     Type string `json:"type"`
     48 
     49     //Data 消息的数据
     50     Data string `json:"data"`
     51 }
     52 
     53 //LoginMsg 登录消息结构体
     54 type LoginMsg struct {
     55     //用户ID
     56     UserID int `json:"userid"`
     57     //用户名
     58     UserName string `json:"username"`
     59     //用户密码
     60     UserPsw string `json:"userpsw"`
     61 }
     62 
     63 //LoginResMsg 登录返回信息结构体
     64 /*
     65     状态值
     66     200        登陆成功
     67     404        未注册
     68     300        账号密码错误
     69     505        服务器内部错误
     70     ...
     71 
     72 */
     73 type LoginResMsg struct {
     74     Code int `json:"code"`
     75     //错误信息
     76     Error string `json:"error"`
     77     //存放在线所有用的id
     78     UsersID []int `json:"usersid"`
     79 }
     80 
     81 //RegisterMsg 注册
     82 type RegisterMsg struct {
     83     User User `json:"user"`
     84 }
     85 
     86 //RegisterResMsg 注册返回信息
     87 //Code    400表示 用户已存在 200 表示注册成功
     88 type RegisterResMsg struct {
     89     Code int `json:"code"`
     90     //错误信息
     91     Error string `json:"error"`
     92 }
     93 
     94 /*PushUserStatusMsg 推送用户状态信息
     95 
     96 UserID    用户ID
     97 
     98 Status    用户状态信息值
     99 */
    100 type PushUserStatusMsg struct {
    101     UserID int `json:"userid"`
    102 
    103     Status int `json:"status"`
    104 }
    105 
    106 /*
    107 SmsMsg 发送的消息结构体
    108 
    109 Content    发送的内容
    110 
    111 User 匿名结构体 继承关系 user.go 下的 User 结构体
    112 */
    113 type SmsMsg struct {
    114     Content string `json:"content"`
    115     User           //匿名结构体 继承关系 user.go 下的 User 结构体
    116 }
    message.go
    1 package message
    2 
    3 //User 用户结构体
    4 type User struct {
    5     UserID     int    `json:"userid"`     //用户通讯ID
    6     UserName   string `json:"username"`   //用户名称昵称
    7     UserPsw    string `json:"userpsw"`    //用户密码
    8     UserStatus int    `json:"userstatus"` //用户状态值
    9 }
    user.go

    服务端sever

     1 package main
     2 
     3 import (
     4     "ChatRoom/sever/model"
     5     "ChatRoom/sever/processor"
     6 
     7     "fmt"
     8     "net"
     9     "time"
    10 )
    11 
    12 //Process 处理客户端-服务端之间的通信
    13 func Process(conn net.Conn) {
    14 
    15     defer conn.Close()
    16     //创建主控
    17     processor := &processor.Processor{
    18         Conn: conn,
    19     }
    20     err := processor.Process2()
    21     if err != nil {
    22         fmt.Println("客户端 与 服务端之间的协程 出错", err)
    23     }
    24 }
    25 
    26 //InitUserDao 初始化UserDao
    27 func InitUserDao() {
    28     fmt.Println("初始化UserDao...")
    29     model.MyUserDao = model.NewUserDao(model.Pool)
    30 }
    31 
    32 func main() {
    33 
    34     //当服务器启动时,初始化连接池
    35     model.InitPool("127.0.0.1:6379", 16, 0, 100*time.Second)
    36 
    37     //初始化pool
    38     InitUserDao()
    39 
    40     //提示信息
    41     fmt.Println("服务端正在使用新结构[MVC布局],使用9000端口监听")
    42     /* net.Listen("tcp","服务器监听IP:端口")
    43     可以把这里的IP改成自己的公网IP,客户端连接处改成公网IP就可以实现联网聊天了
    44     客户端和服务端的端口和设置的IP一致才可以
    45     */
    46     l, err := net.Listen("tcp", "127.0.0.1:9000")
    47     defer l.Close()
    48     if err != nil {
    49         fmt.Println("连接服务器出错	", err)
    50         return
    51     }
    52 
    53     //监听成功,等待客户端连接...
    54     for {
    55         fmt.Println("等待客户端连接...")
    56         conn, err := l.Accept()
    57         if err != nil {
    58             fmt.Println("l.Accept 错误	", err)
    59         }
    60 
    61         //连接服务器成功,起协程服务器客户端-服务端的通信
    62         go Process(conn)
    63     }
    64 }
    main.go

     1 package model
     2 
     3 import "errors"
     4 
     5 //自定义错误
     6 /*
     7 ErrorUserIsNotFound    用户不存在
     8 
     9 ErrorUserExist    用户已存在
    10 
    11 ErrorUserPsw    用户名或密码错误
    12 */
    13 var (
    14     ErrorUserIsNotFound = errors.New("用户不存在")
    15     ErrorUserExist      = errors.New("用户已存在")
    16     ErrorUserPsw        = errors.New("用户名或密码错误")
    17 )
    error.go
     1 package model
     2 
     3 import (
     4     "fmt"
     5     "time"
     6 
     7     "github.com/gomodule/redigo/redis"
     8 )
     9 
    10 //Pool 连接池
    11 var Pool *redis.Pool
    12 
    13 //InitPool 初始化连接池
    14 func InitPool(ip string, maxIdle int, maxActive int, idleTimeout time.Duration) {
    15     fmt.Println("初始化pool...")
    16     Pool = &redis.Pool{
    17         MaxIdle:     maxIdle,     //最大空闲连接数
    18         MaxActive:   maxActive,   //和数据库最大连接数 0 无限制
    19         IdleTimeout: idleTimeout, //最大空闲时间
    20         Dial: func() (redis.Conn, error) {
    21             return redis.Dial("tcp", ip)
    22         },
    23     }
    24 }
    redis
    1 package model
    2 
    3 //User 用户结构体
    4 type User struct {
    5     UserID   int    `json:"userid"`
    6     UserName string `json:"username"`
    7     UserPsw  string `json:"userpsw"`
    8 }
    user.go
      1 package model
      2 
      3 import (
      4     "ChatRoom/conmon/message"
      5     "encoding/json"
      6     "fmt"
      7 
      8     "github.com/gomodule/redigo/redis"
      9 )
     10 
     11 //MyUserDao 全局变量
     12 var MyUserDao *UserDao
     13 
     14 //UserDao 操作结构体
     15 type UserDao struct {
     16     pool *redis.Pool
     17 }
     18 
     19 //NewUserDao UserDao工厂模式(构造函数)
     20 func NewUserDao(pool *redis.Pool) (userdao *UserDao) {
     21     userdao = &UserDao{
     22         pool: pool,
     23     }
     24     return
     25 }
     26 
     27 //GetUserID 获取用户ID
     28 //根据用户提供的ID,返回一个用户实例+err错误信息
     29 func (ud *UserDao) GetUserID(conn redis.Conn, id int) (user *User, err error) {
     30     //fmt.Println("进入 GetUserID方法")
     31     //查询用户信息
     32     res, err := redis.String(conn.Do("hget", "user", id))
     33     //fmt.Println("=================", err)
     34     if err != nil {
     35         fmt.Println("redis.String(conn.Do 进入!=nil")
     36         //如果错误=nil  表示在数据库redis中未找到这个用户
     37         if err == redis.ErrNil {
     38             fmt.Println("进入了edis.ErrNil  ")
     39             err = ErrorUserIsNotFound
     40             return
     41         }
     42         return
     43     }
     44     user = &User{}
     45     //将res反序列化为 User实例
     46     err = json.Unmarshal([]byte(res), user)
     47     if err != nil {
     48         fmt.Println("将res反序列化为 User实例失败", err)
     49         return
     50     }
     51 
     52     return
     53 }
     54 
     55 //Login 登录验证
     56 /*
     57 若 帐号密码不对 则返回 空结构体,err = ErrorUserPsw
     58 若 ID未找到        则返回 空结构体,err = ErrorUserIsNotFound
     59 */
     60 func (ud *UserDao) Login(userID int, userPsw string) (user *User, err error) {
     61     /* 获取连接池中的连接 */
     62     conn := ud.pool.Get()
     63     defer conn.Close()
     64 
     65     /* 调用获取信息方法 */
     66     user, err = ud.GetUserID(conn, userID)
     67     if err != nil {
     68         fmt.Println("d.GetUserID err", err)
     69         return
     70     }
     71 
     72     /* 成功获取用户信息 */
     73     /* 校验密码 */
     74     if user.UserPsw != userPsw {
     75         err = ErrorUserPsw
     76         return
     77     }
     78     return
     79 }
     80 
     81 //Register 注册
     82 func (ud *UserDao) Register(user *message.User) (err error) {
     83     fmt.Println("进入注册方法...")
     84     /* 获取连接池中的连接 */
     85     conn := ud.pool.Get()
     86     defer conn.Close()
     87 
     88     /* 调用获取信息方法 */
     89     _, err = ud.GetUserID(conn, user.UserID)
     90     fmt.Println("***************", err)
     91     if err == nil {
     92         fmt.Println("进入用户已存在...")
     93         //说明用户已存在
     94         err = ErrorUserExist
     95         return
     96     }
     97 
     98     /*执行到这里,表示ID在数据库中不存在,可以注册*/
     99     data, err := json.Marshal(user)
    100     if err != nil {
    101         fmt.Println("进入序列化失败 ...")
    102         return
    103     }
    104 
    105     /* 将ID做key ,data做value 存入数据库user中 */
    106     _, err = conn.Do("hset", "user", user.UserID, string(data))
    107     if err != nil {
    108         fmt.Println("将注册的用户信息存入数据库失败...", err)
    109         return
    110     }
    111     return
    112 }
    userdao.go

     1 package process
     2 
     3 import (
     4     "ChatRoom/conmon/message"
     5     "ChatRoom/sever/utils"
     6     "encoding/json"
     7     "fmt"
     8     "net"
     9 )
    10 
    11 //SmsProcess 服务端消息发送结构体
    12 type SmsProcess struct {
    13 }
    14 
    15 //PushMsg 转发信息
    16 func (sp *SmsProcess) PushMsg(msg *message.Message) {
    17 
    18     /*实例化smsMsg*/
    19     var smsMsg message.SmsMsg
    20     /*反序列化msg.Data*/
    21     err := json.Unmarshal([]byte(msg.Data), &smsMsg)
    22     if err != nil {
    23         fmt.Println("反序列化 msg.Data失败!")
    24         return
    25     }
    26 
    27     /*序列化msg*/
    28     data, err := json.Marshal(msg)
    29     if err != nil {
    30         fmt.Println("反序列化msg 失败!")
    31         return
    32     }
    33 
    34     //遍历服务器端的map,将信息转发给在线用户
    35     for id, up := range userManager.OnLineUsers {
    36 
    37         //过滤掉自己,不要发送消息给自己
    38         if id == smsMsg.UserID {
    39             continue
    40         }
    41         sp.PushMsgToAllOnLineUser(data, up.Conn)
    42     }
    43 
    44 }
    45 
    46 //PushMsgToAllOnLineUser 推送转发消息给在线的所有用户
    47 func (sp *SmsProcess) PushMsgToAllOnLineUser(data []byte, conn net.Conn) {
    48 
    49     tf := &utils.Transfer{
    50         Conn: conn,
    51     }
    52 
    53     err := tf.WritePkg(data)
    54     if err != nil {
    55         fmt.Println("发送消息失败...	", err)
    56     }
    57 }
    smsProcess.go
     1 package process
     2 
     3 import "fmt"
     4 
     5 var (
     6     userManager *UserManager
     7 )
     8 
     9 //UserManager 在线用户结构体
    10 type UserManager struct {
    11     OnLineUsers map[int]*UserProcess
    12 }
    13 
    14 func init() {
    15     userManager = &UserManager{
    16         OnLineUsers: make(map[int]*UserProcess, 1024),
    17     }
    18 }
    19 
    20 //AddOnLineUserS 添加在线用户
    21 func (um *UserManager) AddOnLineUserS(up *UserProcess) {
    22     um.OnLineUsers[up.UserID] = up
    23 }
    24 
    25 //DelOnLineUsers 删除在线用户
    26 func (um *UserManager) DelOnLineUsers(userid int) {
    27     delete(um.OnLineUsers, userid)
    28 }
    29 
    30 //GetAllOnLineUsers 获取所有在线用户
    31 func (um *UserManager) GetAllOnLineUsers() map[int]*UserProcess {
    32     return um.OnLineUsers
    33 }
    34 
    35 //GetOnLineUserToID 根据用户ID查找现在用户
    36 func (um *UserManager) GetOnLineUserToID(userid int) (up *UserProcess, err error) {
    37     up, ok := um.OnLineUsers[userid]
    38     if ok {
    39         return
    40     }
    41     //自定义格式化错误
    42     err = fmt.Errorf("ID:%d 不在线", userid)
    43 
    44     return
    45 
    46 }
    userManager.go
      1 package process
      2 
      3 import (
      4     "ChatRoom/conmon/message"
      5     "ChatRoom/sever/model"
      6     "ChatRoom/sever/utils"
      7     "encoding/json"
      8     "fmt"
      9     "net"
     10 )
     11 
     12 //UserProcess 用户处理器
     13 type UserProcess struct {
     14     Conn net.Conn
     15     //UserId 该字段表示是哪一个用户
     16     UserID int
     17 }
     18 
     19 //NoticeOnLineUsers 通知在线的用户 userid 上线了
     20 func (thisUP *UserProcess) NoticeOnLineUsers(userid int) {
     21     for id, up := range userManager.OnLineUsers {
     22         //过滤掉自己
     23         if id == userid {
     24             continue
     25         }
     26         //开始通知其他人
     27         up.NoticeMeOnLine(userid)
     28     }
     29 }
     30 
     31 //NoticeMeOnLine 通知我上线了
     32 func (thisUP *UserProcess) NoticeMeOnLine(userid int) {
     33     /*实例化父消息*/
     34     var msg message.Message
     35     msg.Type = message.PushUserStatusMsgType
     36 
     37     /*实例化子消息*/
     38     var pushUserStatusMsg message.PushUserStatusMsg
     39     pushUserStatusMsg.UserID = userid
     40     pushUserStatusMsg.Status = message.OnLine
     41 
     42     /*序列化 子消息结构体*/
     43     data, err := json.Marshal(pushUserStatusMsg)
     44     if err != nil {
     45         fmt.Println("序列化子消息结构体(pushUserStatusMsg)失败!")
     46         return
     47     }
     48 
     49     /*将子消息序列化后的封装体data 复制给 父消息结构体中的 data*/
     50     msg.Data = string(data)
     51 
     52     /*序列化 父消息结构体*/
     53     data, err = json.Marshal(msg)
     54     if err != nil {
     55         fmt.Println("父消息结构体(msg)序列化失败!")
     56         return
     57     }
     58 
     59     /*创建 Tranfer实例*/
     60     tf := utils.Transfer{
     61         Conn: thisUP.Conn,
     62     }
     63     /*发送 父消息 */
     64     err = tf.WritePkg(data)
     65     if err != nil {
     66         fmt.Println("NoticeMeOnLine发送通知失败!")
     67         return
     68     }
     69 
     70 }
     71 
     72 //SeverProcessRegister 只处理用户注册请求
     73 func (thisUP *UserProcess) SeverProcessRegister(msg *message.Message) (err error) {
     74 
     75     /*1    先从msg中取出msg.data,并进行反序列化为registerMsg操作*/
     76     var registerMsg message.RegisterMsg
     77     err = json.Unmarshal([]byte(msg.Data), &registerMsg)
     78     if err != nil {
     79         fmt.Println("反序列化 registerMsg失败...")
     80         return
     81     }
     82 
     83     //2    声明 resMsg
     84     var resMsg message.Message
     85     resMsg.Type = message.RegisterResMsgType
     86     //3    声明 registerResMsg
     87     var registerResMsg message.RegisterResMsg
     88 
     89     //3    反序列化成功,去数据库完成注册
     90     err = model.MyUserDao.Register(&registerMsg.User)
     91 
     92     if err != nil {
     93         if err == model.ErrorUserExist {
     94             registerResMsg.Code = 500
     95             registerResMsg.Error = model.ErrorUserExist.Error()
     96         } else if err == model.ErrorUserIsNotFound {
     97             registerResMsg.Code = 404
     98             registerResMsg.Error = model.ErrorUserIsNotFound.Error()
     99         } else {
    100             fmt.Println("注册发生错误...")
    101         }
    102     } else {
    103         registerResMsg.Code = 200
    104         fmt.Println("注册成功!")
    105     }
    106 
    107     //4    序列化 registerResMsg
    108     data, err := json.Marshal(registerResMsg)
    109     if err != nil {
    110         fmt.Println("序列化registerResMsg失败.", err)
    111         return
    112     }
    113 
    114     //5    将data 赋值给 resMsg(message.Message)
    115     resMsg.Data = string(data)
    116 
    117     //6 将resMsg进行序列化
    118     data, err = json.Marshal(resMsg)
    119     if err != nil {
    120         fmt.Println("序列化resMsg失败...", err)
    121         return
    122     }
    123 
    124     //7    发送 数据
    125     //因为使用分布式mvc,我们先创建一个Transfer实例,然后读取
    126     tf := &utils.Transfer{
    127         Conn: thisUP.Conn,
    128     }
    129     err = tf.WritePkg(data)
    130 
    131     return
    132 }
    133 
    134 //SeverProcessLogin 只处理登录请求
    135 func (thisUP *UserProcess) SeverProcessLogin(msg *message.Message) (err error) {
    136 
    137     /*先从msg中取出msg.data,并进行反序列化操作*/
    138     var loginmsg message.LoginMsg
    139     err = json.Unmarshal([]byte(msg.Data), &loginmsg)
    140     if err != nil {
    141         fmt.Println("反序列化&loginmsg失败...")
    142         return
    143     }
    144 
    145     //1    声明 resMsg
    146     var resMsg message.Message
    147     resMsg.Type = message.LoginResMsgType
    148     //2    声明 loginResMsg
    149     var loginResMsg message.LoginResMsg
    150 
    151     //3    反序列化成功登录验证
    152     user, err := model.MyUserDao.Login(loginmsg.UserID, loginmsg.UserPsw)
    153     if err != nil {
    154 
    155         if err == model.ErrorUserIsNotFound {
    156             loginResMsg.Code = 404
    157             loginResMsg.Error = err.Error()
    158         } else if err == model.ErrorUserPsw {
    159             loginResMsg.Code = 300
    160             loginResMsg.Error = err.Error()
    161 
    162         } else {
    163             loginResMsg.Code = 505
    164             loginResMsg.Error = err.Error()
    165         }
    166 
    167     } else {
    168         loginResMsg.Code = 200
    169         fmt.Printf("%v 登陆成功!", user.UserName)
    170 
    171         /*登陆成功后,将登陆成功的用户放到userManager中*/
    172         thisUP.UserID = loginmsg.UserID
    173         userManager.AddOnLineUserS(thisUP)
    174 
    175         /*调用通知在线用户,ID上线了的方法,将登陆ID传入*/
    176         thisUP.NoticeOnLineUsers(loginmsg.UserID)
    177 
    178         /*循环将在线用户的ID添加到LoginResMsg.UsersID切片中*/
    179         for id := range userManager.OnLineUsers {
    180             loginResMsg.UsersID = append(loginResMsg.UsersID, id)
    181         }
    182     }
    183 
    184     //3    序列化 loginResMsg
    185     data, err := json.Marshal(loginResMsg)
    186     if err != nil {
    187         fmt.Println("序列化loginResMsg失败.", err)
    188         return
    189     }
    190 
    191     //4    将data 赋值给 resMsg(message.Message)
    192     resMsg.Data = string(data)
    193 
    194     //5 将resMsg进行序列化
    195     data, err = json.Marshal(resMsg)
    196     if err != nil {
    197         fmt.Println("序列化resMsg失败...", err)
    198         return
    199     }
    200 
    201     //6    发送 数据
    202     //因为使用分布式mvc,我们先创建一个Transfer实例,然后读取
    203     tf := &utils.Transfer{
    204         Conn: thisUP.Conn,
    205     }
    206     err = tf.WritePkg(data)
    207 
    208     return
    209 }
    userProcess.go

     1 package processor
     2 
     3 import (
     4     "ChatRoom/conmon/message"
     5     "ChatRoom/sever/process"
     6     "ChatRoom/sever/utils"
     7     "fmt"
     8     "io"
     9     "net"
    10 )
    11 
    12 //Processor 结构体
    13 type Processor struct {
    14     Conn net.Conn
    15 }
    16 
    17 //SeverProcessMsg 根据客户端发送消息的不同,决定调用哪个函数来处理
    18 func (pcs *Processor) SeverProcessMsg(msg *message.Message) (err error) {
    19 
    20     //测试一下是否能接收到来自客户端的群发消息
    21     fmt.Println("msg:", msg)
    22 
    23     switch msg.Type {
    24     case message.LoginMsgType:
    25         //处理登录逻辑...
    26         //创建一个UserProcess 实例
    27         up := &process.UserProcess{
    28             Conn: pcs.Conn,
    29         }
    30         err = up.SeverProcessLogin(msg)
    31     case message.RegisterMsgType:
    32         //处理注册逻辑...
    33         //创建一个UserProcess 实例
    34         up := &process.UserProcess{
    35             Conn: pcs.Conn,
    36         }
    37         err = up.SeverProcessRegister(msg)
    38     case message.SmsMsgType:
    39         //创建一个SmsMsg实例
    40 
    41         smsProcess := &process.SmsProcess{}
    42         smsProcess.PushMsg(msg)
    43     default:
    44         fmt.Println("消息类型不存在,无法处理...")
    45     }
    46     return
    47 }
    48 
    49 //Process2 第二层处理
    50 func (pcs *Processor) Process2() (err error) {
    51     //读取客户端发送的信息
    52 
    53     for {
    54 
    55         tf := utils.Transfer{
    56             Conn: pcs.Conn,
    57         }
    58 
    59         fmt.Println("读取来自客户端的数据中...")
    60         msg, err := tf.ReadPkg()
    61 
    62         if err != nil {
    63             if err == io.EOF {
    64                 fmt.Println("客户端断开连接,服务器退出...")
    65                 return err
    66             }
    67             fmt.Println("ReadPkg()错误", err)
    68             return err
    69 
    70         }
    71         //fmt.Println("msg", msgs)
    72         err = pcs.SeverProcessMsg(&msg)
    73         if err != nil {
    74             return err
    75         }
    76     }
    77 }
    processor.go

     1 package utils
     2 
     3 import (
     4     "ChatRoom/conmon/message"
     5     "encoding/binary"
     6     "encoding/json"
     7     "fmt"
     8     "net"
     9 )
    10 
    11 //Transfer 将下列方法关联到结构体中
    12 type Transfer struct {
    13     //分析有哪些字段
    14     Conn net.Conn   //
    15     Buf  [8096]byte //缓冲区
    16 }
    17 
    18 //ReadPkg 读取封包数据
    19 func (tf *Transfer) ReadPkg() (msg message.Message, err error) {
    20 
    21     //buf := make([]byte, 8096)
    22 
    23     _, err = tf.Conn.Read(tf.Buf[:4])
    24 
    25     if err != nil {
    26         //自定义错误
    27         //err = errors.New("读取数据 头部 错误")
    28         return
    29     }
    30     //根据buf[:4] 转换成一个uint32
    31     var pkgLen uint32
    32     pkgLen = binary.BigEndian.Uint32(tf.Buf[0:4])
    33 
    34     //根据 pkgLen的长度读取内容 到 buf中
    35     n, err := tf.Conn.Read(tf.Buf[:pkgLen])
    36     if n != int(pkgLen) || err != nil {
    37         //自定义错误
    38         //err = errors.New("读取数据 内容 错误")
    39         return
    40     }
    41 
    42     //将pkglen 反序列化为 message.Massage  一定+&
    43     err = json.Unmarshal(tf.Buf[:pkgLen], &msg)
    44     if err != nil {
    45         fmt.Println("反序列化失败!	", err)
    46         return
    47     }
    48     return
    49 
    50 }
    51 
    52 //WritePkg 发送数据
    53 func (tf *Transfer) WritePkg(data []byte) (err error) {
    54     //1 发送信息长度
    55     var msgLen uint32 = uint32(len(data))
    56     //  var buf [4]byte
    57     //将 信息长度msgLen 转换为 byte[]切片
    58     binary.BigEndian.PutUint32(tf.Buf[0:4], msgLen)
    59 
    60     //发送信息长度
    61     r, err := tf.Conn.Write(tf.Buf[:4])
    62     if r != 4 || err != nil {
    63         fmt.Println("消息长度发送失败!	", err)
    64         return
    65     }
    66     //2    发送信息内容
    67     r, err = tf.Conn.Write(data)
    68     if r != int(msgLen) || err != nil {
    69         fmt.Println("消息长度发送失败!	", err)
    70         return
    71     }
    72     return
    73 }
    utils.go
    时间若流水,恍惚间逝去
  • 相关阅读:
    [Canvas]英雄可以射箭了
    [Canvas]人物型英雄出现(前作仅为箭头)
    day33_Spring学习回顾_01
    day33_Spring学习笔记_01
    MyEclipse中,当我们写一个类实现一个接口时,会自动生成重写该接口的方法,但是,方法的参数提示不够好,是什么原因导致的呢?该如何解决呢?
    使用Junit测试一个 spring静态工厂实例化bean 的例子,所有代码都没有问题,但是出现java.lang.IllegalArgumentException异常
    MyEclipse中如何变换查看项目文件夹
    实战网卡bond
    Centos7上使用官方YUM源安装Mysql
    CentOS 7上安装WordPress详细步骤
  • 原文地址:https://www.cnblogs.com/alanshreck/p/14159021.html
Copyright © 2020-2023  润新知