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

  • GO语言综合项目

  •   包含:

  •     1:GO语言基础知识

  •     2:TCP-Socket网络编程

  •     3:Redis数据库

  •   已实现:

  •     登录

  •       查看在线用户

  •       群聊

  •       私聊(未实现)

  •       历史消息(未实现)

  •     注册

  •     退出

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

 

 

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

 

 

 

  

 

  • 整体结构

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

 

  • 客户端client

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

 

  •     

 

 

  • GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
 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\t\t登录系统\n2\t\t注册账户\n3\t\t退出系统\n请输入(1-3)数字进行操作\n")
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
  • GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
 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
  • GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
 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("客户端读取服务器信息错误!\t", 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("返回的消息类型,暂时无法处理..\n")
88         }
89 
90     }
91 }
server.go
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
 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\t%v", smsmsg.UserID, smsmsg.Content)
21     fmt.Println(talking)
22     fmt.Println()
23 }
smsManger.go
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
 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
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
 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:\t%v\n", 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
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
  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("连接服务器失败\t", 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("序列化失败! \t", 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("序列化失败! \t", 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("客户端:注册时发送数据错误! \t", 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("连接服务器失败\t", 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("序列化失败! \t", 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("序列化失败! \t", 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("消息长度发送失败!\t", err)
151         return
152     }
153     //fmt.Printf("发送长度成功,长度:%v\t内容:%v", len(data2), string(data2))
154 
155     // 发送消息数据本身
156     _, err = conn.Write(data2)
157     if err != nil {
158         fmt.Println("消息长度发送失败!\t", 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("\n")
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
  • GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
 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("反序列化失败!\t", 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("消息长度发送失败!\t", err)
64         return
65     }
66     //2    发送信息内容
67     r, err = tf.Conn.Write(data)
68     if r != int(msgLen) || err != nil {
69         fmt.Println("消息长度发送失败!\t", err)
70         return
71     }
72     return
73 }
utils.go

 

 

  • 共用项common

  • GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
  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
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
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

  • GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
  • GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
 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("连接服务器出错\t", 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 错误\t", err)
59         }
60 
61         //连接服务器成功,起协程服务器客户端-服务端的通信
62         go Process(conn)
63     }
64 }
main.go
  • GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
 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
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
 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
GoLang 海量用户聊天系统(TCP-Socket网络编程+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
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
  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
  • GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
 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("发送消息失败...\t", err)
56     }
57 }
smsProcess.go
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
 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
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
  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
  • GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
 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
  • GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
GoLang 海量用户聊天系统(TCP-Socket网络编程+Redis数据库)
 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("反序列化失败!\t", 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("消息长度发送失败!\t", err)
64         return
65     }
66     //2    发送信息内容
67     r, err = tf.Conn.Write(data)
68     if r != int(msgLen) || err != nil {
69         fmt.Println("消息长度发送失败!\t", err)
70         return
71     }
72     return
73 }
utils.go

 

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

上一篇:nodejs-xss防御


下一篇:非关系型数据库