P323开始(尚硅谷GO教程)老韩又改目录结构了,没办法,和之前一样,先说下目录结构,再给代码,部分代码在之前讲过,还有知识的话由于本人近期很忙,所以这些就不多赘述了,读者可自行查阅官方文档(GO中文标准库)或其他网站。
目录
- 框架与目录结构
- utils.go
- userProcess.go
- processor.go
- server的main
- login.go
框架与目录结构
老韩讲的是分层架构,就和MVC差不多,服务器框架图如下
目录结构
海量用户通信系统/
├── go.mod
├── client/
│ ├── main.go
│ └── login.go
├── server/
│ └── main.go
└── common/├── message/│ └── message.go└── utils/└── utils.go
utils.go
package utilsimport ("MassUsrsCom/common/message""encoding/binary""encoding/json""fmt""net"
)// 这里将这些方法关联到结构体中
type Transfer struct {//分析它应该有哪些字段Conn net.ConnBuf [8096]byte //传输时使用的缓冲
}// 可以自定义错误变量err,即定义一个新的err,写入自定义的错误内容,
// 但考虑到上层可能要判断err的原本的类型,所以这里直接返回err
// 读数据包
func (tf *Transfer) ReadPkg() (mes message.Message, err error) {fmt.Println("读数据包...")buf := tf.Buf[:] //1.准备缓冲区//后续可能会读取更多字节,所以缓冲区大小一般都设置得比较大//2.读取消息头部并存入缓冲区n, err := tf.Conn.Read(buf[:4])//conn.Read 在conn没有被关闭的情况下,才会阻塞//如果客户端关闭了conn,则不会阻塞if n != 4 || err != nil {//因为服务端要通过判断err的类型来提示客户端关闭,所以这里什么都不做return}//3.将缓冲区的消息头部转换成消息长度var pkgLen = binary.BigEndian.Uint32(buf[:4])//4.根据消息长度读取消息内容n, err = tf.Conn.Read(buf[:pkgLen])if n != int(pkgLen) || err != nil {fmt.Println("conn.Read error")return}//5.将消息内容反序列化并返回err = json.Unmarshal(buf[:pkgLen], &mes)//由于 mes 是在函数签名中命名的返回值变量,Go 自动为它创建了一个//初始的 message.Message 类型实例,这样就无需显式声明if err != nil {fmt.Println("read pkg body error,json.Unmarshal error")return}return
}// 写数据包
func (tf *Transfer) WritePkg(data []byte) (err error) {fmt.Println("写数据包...")buf := tf.Buf[:4] //1.准备缓冲区//2.根据消息内容获取消息长度var pkgLen = uint32(len(data))//3.将消息长度存入缓冲区binary.BigEndian.PutUint32(buf, pkgLen)//4.发送消息长度n, err := tf.Conn.Write(buf)if n != 4 || err != nil {fmt.Println("conn.Write error")return}//5.发送消息内容n, err = tf.Conn.Write(data)if n != int(pkgLen) || err != nil {fmt.Println("conn.Write error")return}return
}
userProcess.go
smsProcess暂时声明一个process包,userProcess如下:
package processimport ("MassUsrsCom/common/message""MassUsrsCom/server/utils""encoding/json""fmt""net"
)type UserProcess struct {Conn net.Conn
}// 处理登录消息
func (up *UserProcess) ServerProcessLogin(mes *message.Message) (err error) {//1.先从mes中取出mes.Data,并直接反序列化成LoginMesvar loginMes message.LoginMeserr = json.Unmarshal([]byte(mes.Data), &loginMes)if err != nil {fmt.Println("json.Unmarshal error:")return}//1初始化一个Mes 结构体var resMes message.MessageresMes.Type = message.LoginResMesType//2创建一个LoginResMes 结构体var loginResMes message.LoginResMes//如果用户id=100,密码=123456,认为合法,否则不合法if loginMes.UserID == 100 && loginMes.UserPwd == "123456" {//合法loginResMes.Code = 200} else {//不合法loginResMes.Code = 500 //状态码,表示该用户不存在loginResMes.Error = "该用户不存在,请注册再使用"}//3 将loginResMes序列化data, err := json.Marshal(loginResMes)if err != nil {fmt.Println("json.Marshal error:")return}//4.将序列化后的loginResMes作为给resMes的dataresMes.Data = string(data)//5.对resMes进行序列化,准备发送data, err = json.Marshal(resMes)if err != nil {fmt.Println("json.Marshal error:")return}//6.发送消息//因为使用分层模式(mvc),我们先创建一个Transfer实例,然后读取tf := &utils.Transfer{Conn: up.Conn}err = tf.WritePkg(data)if err != nil {fmt.Println("WritePkg(conn) error:")return}return
}
processor.go
package mainimport ("MassUsrsCom/common/message""MassUsrsCom/server/process""MassUsrsCom/server/utils""fmt""io""net"
)type Processor struct {Conn net.Conn
}// 判断并处理不同种类的消息
func (proc *Processor) serverProcessMes(mes *message.Message) (err error) {switch mes.Type {case message.LoginMesType://处理登录逻辑//创建一个UserProcess实例up := &process.UserProcess{Conn: proc.Conn}err = up.ServerProcessLogin(mes)case message.RegisterMesType://处理注册default:fmt.Println("消息类型不存在,无法处理...")}return
}
func (proc *Processor) process2() (err error) {//循环读取客户端发送的信息for {//创建Transfer实例完成读包的任务tf := &utils.Transfer{Conn: proc.Conn}mes, err := tf.ReadPkg() //读取客户端消息if err != nil {if err == io.EOF {fmt.Println("客户端退出,相关的服务器协程也退出...")return err} else {fmt.Println(err)return err}}err = proc.serverProcessMes(&mes) //处理客户端的消息if err != nil {fmt.Println(err)return err}}
}
server的main
package mainimport ("fmt""net"
)// 处理和客户端的通信
func goroutine(conn net.Conn) {//这里需要延时关闭conndefer conn.Close()//这里调用总控,创建一个processor := &Processor{Conn: conn}err := processor.process2()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 goroutine(conn)}
}
login.go
package mainimport ("MassUsrsCom/common/message""MassUsrsCom/server/utils""encoding/json""fmt""net"
)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部分tf := utils.Transfer{Conn: conn}err = tf.WritePkg(data)if err != nil {fmt.Println("WritePkg(conn) error:", err)return}//8.接收服务器端返回的信息(消息mes或错误)mes, err = tf.ReadPkg() //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
}