开发者学堂课程【Go 语言核心编程 - 面向对象、文件、单元测试、反射、TCP 编程:海量用户通讯系统——服务端结构改进2】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/626/detail/9804
海量用户通讯系统-服务端结构改进2
一、更改方法
1、步骤:
(1)首先把分析出来的文件创建好,然后放入相应的文件夹中(实际就是包,因为在Go里面文件夹就体现一种包,因为一般来说一个文件夹就对应一个包)
(2) 现在根据各个文件,完成的任务不同,将 main.go 的文件剥离到对应的文件中即可
(3) 先修改了 utils/utils.go
package utils
Import (
“fmt”
“net”
“go_code/chatroom/common/message
“encoding/binary”
“encoding/json”
)
//这里将这些方法关联到结构体中
type Transfer struct {
//分析它应该有哪些字段
conn net.conn
Buf [8096]byte
//这时传输时,使用缓冲
}
func (this *Transfer) ReadPkg() (mes message.Message, err error) {
//buf :=make([ ]byte, 8096)
fmt.PrintIn(“读取客户端发送的数据”)
//conn.Read 在conn没有被关闭的情况下才会阻塞
//如果客户端关闭了 conn 则,就不会阻塞
_,err = this.conn.Read(this.Buf[:4])
If err != nil {
//err = errors.New(“read pkg header error”)
return
}
//根据buf[:4] 转成一个 uint32类型
var pkgLen uint32
pkgLen =binary.BigEndian.uint32(this.Buf[0:4])
//根据 pkgLen 读取消息内容
n, err :=this.Conn.Read(this.Buf[ :pkgLen])
if n != int(pkgLen) II err != nil {
//err = errors.New(“read pkg body error”)
return
}
//把pkgLen 反序列化成 -> message.Message
//技术就是一层窗户纸 &mes! !
err = json.Unmarsha1(this.Buf[:pkgLen], &mes)
if err != nil {
fmt.PrintIn(“json.Unmarsha err”, err)
return
}
return
}
func (this *Transfer) writePkg(data [ ]byte) (err error) {
//先发送一个长度给对方
var pkgLen uint32
pkgLen = uint32(len(data))
//var buf [4]byte
binary.BigEndian.PutUint32(this.Buf[0:4], pkgLen)
//发送长度
n, err : = this.conn.write(this.Buf[:4])
if n != 4 | | err != nil {
fmt.PrintIn(“conn.write(bytes) fail”,err)
return
}
//发送data本身
n, err = this.Conn.write(data)
if n != int(pkgLen) | | err != nil {
fmt.PrintIn(“conn.write(bytes) fail”, err)
return
}
return
}
(4) 修改了process2/userprocess.go
package process2
Import (
“fmt”
“net”
“go_code/chatroom/common/message”
“go_code/chatroom/server/utils”
“encoding/json”
)
type Userprocess struct {
//字段
Conn net.Conn
//
}
//编写一个函数serverProcessLogin函数,专门处理登录请求
fanc (this *UserProcess) ServerProcessLogin(mes *message.Messsage) (err errror) {
//核心代码
//1. 先从mes中取出 mes . Data ,并直接反序列化成LoginMes
var loginMes message.LoginMes
err = json.Unmarshal([ ]byte(mes.Data), &loginMes)
if err != nil {
fmt.PrintIn(“json.Unmarshal err=”, err)
return
}
//1先声明一个 resMes
var resMes message.Message
resMes.Type = message.LoginResMesType
//2先声明一个 LoginResMes,并完成赋值
var loginResMes message.LoginResMes
//如果用户id= 100,密码=123456, 认为合法,否则不合法
if loginMes.UserId == 100 && loginMes.UserPwd ==”12345” {
//合法
loginResMes.Code = 200
} else {
//不合法
loginResMes.Code =500 // 500 状态码,表示该用户不存在
loginResMes.Error = “该用户不存在,请注册后再使用...”
}
//3将 loginResMes 序列化
data, err := json.Marshal(loginResMes)
If err != nil {
fmt.PrintIn(“json.Marshal fail”, err)
return
}
//4. 将data 赋值给resMes
resMes.Data = string(data)
//5. 对resMes 进行序列化,准备发送
data,err = json.Marshal(resMes)
if err != nil {
fmt.PrintIn(“json,Marshal fail”,err)
return
}
//6. 发送data,我们将其装到writePkg函数
//因为使用分层模式(mvc),我们先创建一个Transfer 实例,然后读取
tf := &utils.Transfer {
Conn : this.Conn,
}
err = tf.writePkg(data)
return
}
(5) 修改了 main/processor.go
package main
Import (
“fmt”
“net”
“go_code/chatroom/common/message”
“go_code/chatroom/server/utils”
“go_code/chatroom/server/process”
“io”
)
//先创建一个 Processor 的结构体
type Processor struct {
Conn
}
//编写一个serverProcessMes 函数
//功能: 根据客户端发送消息种类不同,决定调用那个函数来处理
fanc (this *Processor) serverProcessMes(mes *message.Message) (err error) {
switch mes.Type {
case message.LoginMesType :
//处理登录登录
//创建一个UserProcess实例
up := &process2.UserProcess{
Conn : this.Conn,
}
err = up.ServerProcessLogin(mes)
case message.RegisterMesType
//处理注册
default :
Fmt.PrintIn(“消息类型不存在,无法处理...”)
}
Return
}
func (this *Processor) process2() (err error) {
//循环的客户端发生的消息
for {
//这里我们将读取数据包,直接封装成一个函数readPkg( ),返回Message, err
//创建一个Transfer 实例完成读包任务
tf := &utils.Transfer{
Conn : this.Conn,
}
mes, err := tf.ReadPkg( )
if err != nil {
fmt.PrintIn(“客户端退出,服务端也退出..”)
return
}
}
err = this.serverProcessMes(&mes)
if err != nil {
return err
}
}
}
(5)
修改了main/main.go
//处理和客户端的通讯
fanc process(conn net.conn) {
//这里需要延时关闭conn
defer conn.Close( )
//这里调用监控,创建一个
processsor := &Processor{
conn : conn,
}
err := processor.process( )
if err != nil {
fmt.PrintIn(“客户端和服务器通讯承认协程错误=err”, err)
}
}