这个系统其实是尚硅谷的老韩讲的(尚硅谷网络编程项目),但是他讲得很碎片化,思路不够清晰,时间又长,所以要掌握还是挺难的。如果你听了他的视频,不去梳理系统业务流程,不去看代码就往下听,那是很容易懵圈的。好在本人做了登录模块和服务器处理消息的业务流程图,我相信这一定有助于大家整理思路和理解系统业务流程。另外,因为老韩之后把login函数的部分代码封装到单独的包里了,所以我就干脆从头开始快速讲一遍,当然之前讲过的知识不会再讲。
目录
- 一、系统框架搭建
- 二、C/S通信总体流程
- 1.登录模块总体流程
- 2.服务器处理消息总体流程
一、系统框架搭建
首先我们需要搭建好目录结构,目录结构如下
海量用户通信系统/
├── go.mod
├── client/
│ ├── main.go
│ └── login.go
├── server/
│ └── main.go
└── common/├── message/│ └── message.go└── utils/└── utils.go
注意,模块名需要自定义,我的模块名叫MassUsrsCom。搭建好后,按如下步骤构建初步的代码:
1.在message.go中定义消息类型
package messageconst (LoginMesType = "LoginMes"LoginResMesType = "LoginResMes"RegisterMesType = "RegisterMes"
)type Message struct {Type string `json:"type"` //消息类型Data string `json:"data"` //消息
}// 定义两个消息..后面需要再增加
type LoginMes struct {UserID int `json:"userID"` //用户idUserPwd string `json:"userPwd"` //用户密码UserName string `json:"userName"` //用户名
}type LoginResMes struct {Code int `json:"code"` //返回状态码 500表示该用户未注册 200表示登录成功Error string `json:"error"` //返回错误信息
}
type RegisterMes struct {
}
2.在utils.go中声明读数据包和写数据包的函数
package utilsimport ("MassUsrsCom/common/message""encoding/binary""encoding/json""fmt""net"
)// 读数据包
func ReadPkg(conn net.Conn) (mes message.Message, err error) {return
}// 写数据包
func WritePkg(conn net.Conn, data []byte) (err error) {return
}
3.在login.go中声明 login函数
package mainimport ("MassUsrsCom/common/message""MassUsrsCom/common/utils""encoding/json""fmt""net"
)func login(userID int, userPwd string) (err error) {return
}
4.在client包的main.go中写完整的代码
package mainimport ("fmt"
)// 定义两个变量,一个表示用户id,一个表示用户密码
var userID int
var userPwd stringfunc main() {//接收用户的选择var key int//判断是否还继续显示菜单var loop = truefor loop {fmt.Println("------------------欢迎登录多人聊天系统")fmt.Println("\t\t\t 1 登录聊天室")fmt.Println("\t\t\t 2 注册用户")fmt.Println("\t\t\t 3 退出系统")fmt.Println("\t\t\t 请选择(1-3):")fmt.Scanln(&key)switch key {case 1:fmt.Println("登录聊天室")loop = falsecase 2:fmt.Println("注册用户")loop = falsecase 3:fmt.Println("退出系统")loop = falsedefault:fmt.Println("你的输入有误,请重新输入")}}//根据用户输入显示新的提示信息if key == 1 {//说明用户要登录fmt.Printf("请输入用户的id号:")fmt.Scanf("%d\n", &userID)fmt.Printf("请输入用户的密码:")fmt.Scanf("%s\n", &userPwd)login(userID, userPwd)} else if key == 2 {fmt.Println("进行用户注册的逻辑...")}
}
5.在server包的main.go中写部分代码
package mainimport ("MassUsrsCom/common/message""MassUsrsCom/common/utils""encoding/json""fmt""io""net"
)// 处理登录消息
func serverProcessLogin(conn net.Conn, mes *message.Message) (err error) {return
}// 判断并处理不同种类的消息
func serverProcessMes(conn net.Conn, mes *message.Message) (err error) {switch mes.Type {case message.LoginMesType://处理登录逻辑err = serverProcessLogin(conn, mes)case message.RegisterMesType://处理注册default:fmt.Println("消息类型不存在,无法处理...")}return
}// 处理和客户端的通信
func process(conn net.Conn) {
}
func main() {
}
二、C/S通信总体流程
1.登录模块总体流程
流程图
代码
func login(userID int, userPwd string) (err error) {//下一个就要开始定协议// fmt.Printf("userId=%d pwd=%s\n", userId, pwd)// return nil//1.连接到服务器conn, err := net.Dial("tcp", "localhost:8889")if err != nil {fmt.Println("net.Dial error:", err)return}//延时关闭defer conn.Close()//2.初始化一个Mes 结构体var mes message.Messagemes.Type = message.LoginMesType//3.初始化一个LoginMes 结构体var loginMes message.LoginMesloginMes.UserID = userIDloginMes.UserPwd = userPwd//4.将loginMes 序列化data, err := json.Marshal(loginMes)if err != nil {fmt.Println("json.Marshal error:", err)return}//5.将序列化后的loginMes作为mes的Data部分mes.Data = string(data)//6.将mes序列化data, err = json.Marshal(mes)if err != nil {fmt.Println("json.Marshal error:", err)return}//7.发送消息,即mes的Data部分err = utils.WritePkg(conn, data)if err != nil {fmt.Println("WritePkg(conn) error:", err)return}//8.接收服务器端返回的信息(消息mes或错误)mes, err = utils.ReadPkg(conn) //mesif err != nil {fmt.Println("ReadPkg(conn) error:", err)return}//9.将mes的Data部分反序列化成LoginResMesvar loginResMes message.LoginResMeserr = json.Unmarshal([]byte(mes.Data), &loginResMes)if err != nil {fmt.Println("json.Unmarshal error:", err)return}//10.验证LoginResMesif loginResMes.Code == 200 {fmt.Println("登录成功")} else if loginResMes.Code == 500 {fmt.Println(loginResMes.Error)}return
}
2.服务器处理消息总体流程
流程图
代码
// 处理和客户端的通信
func process(conn net.Conn) {//这里需要延时关闭conndefer conn.Close()//循环读取客户端发送的信息for {mes, err := utils.ReadPkg(conn) //读取客户端消息if err != nil {if err == io.EOF {fmt.Println("客户端退出,相关的服务器协程也退出...")return} else {fmt.Println(err)return}}err = serverProcessMes(conn, &mes) //处理客户端的消息if err != nil {fmt.Println(err)return}}
}
func main() {//提示信息fmt.Println("服务器在8889端口监听")listen, err := net.Listen("tcp", "0.0.0.0:8889")if err != nil {fmt.Println("服务器监听端口失败:", err)return}defer listen.Close()//一旦监听成功,就等待客户端来连接服务器for {fmt.Println("等待客户端来连接服务器......")conn, err := listen.Accept()if err != nil {fmt.Println("客户端连接服务器失败:", err)continue}//一旦连接成功,则启动一个协程和客户端保持通信go process(conn)}
}