database.GetUserById函数先执行,defer database.Rdb.Close()后执行,为什么我运行后,就提前报错:redis: client is closed,而且我写在任何位置都不行,而当我删除defer database.Rdb.Close()命令后,就不会有问题,这又是为什么呢?
// loginRequest:登录请求函数
func (p *Process) LoginRequest(mes common.Message) {
defer database.Rdb.Close() //请求登录函数执行完毕或发生错误时,确保关闭 Redis 客户端连接,释放资源并避免潜在的内存泄漏
var loginMes common.LoginMes
err := json.Unmarshal([]byte(mes.MessData), &loginMes)
if err != nil {
fmt.Println("userProcess_server.go ->> LoginRequest ->> json.Unmarshal(...) err:", err)
return
}
//提前定义总消息,和返回结果消息,准备发送给客户端(为了告知客户端用户id是否在服务器端存在)
var resultMessage common.Message
var returnResMes common.ReturnResMes
// //如果用户id=tom,密码123,就认为合法,否则不合法
// if loginMes.UserId == "tom" && loginMes.UserPwd == "123" {
// returnResMes.Code = 1 //1代表真,说明合法
// } else {
// returnResMes.Code = 0 //0代表假,说明不合法,表示用户不存在
// }
status, errResult, err := database.GetUserById(loginMes.UserId, loginMes.UserPwd) //传入用户输入进来的用户名和密码
if err != nil {
fmt.Println("userProcess_server.go ->> database.GetUserById err ->> ", err)
return
}
returnResMes.Code = status
returnResMes.ErrResult = errResult
//将returnResMes 序列化
ret_ByteSli, err := json.Marshal(returnResMes)
if err != nil {
fmt.Println("userProcess_server.go ->> LoginRequest ->> json.Marshal(returnResMes) err err:", err)
return
}
//将返回类型和返回结果的两个消息赋给resultMessage(总消息)
resultMessage.MessData = string(ret_ByteSli)
resultMessage.MessType = common.ReturnResMesType
//对resultMessage进行序列化
res_ByteSli, err := json.Marshal(resultMessage)
if err != nil {
fmt.Println("userProcess_server.go ->> LoginRequest ->> json.Marshal(resultMessage) err:", err)
return
}
//发送总消息(发送后,客户端那边还需要反序列化,客户端收到消息1,表明用户存在,收到消息0,表面用户不存在)
//因为使用分层模式(mvc),先创建一个Transfer实例,然后发送结果消息
tf := common.NewTransfer(p.Conn)
err = tf.WritePKG(res_ByteSli)
if err != nil {
fmt.Println("userProcess_server.go ->> LoginRequest ->> tf.WritePKG(res_ByteSli) err:", err)
return
}
if returnResMes.Code == 1 { //如果状态码被赋值1,说明用户存在,则可以进行一些逻辑操作
fmt.Printf("%v 连接到服务器\n", p.Conn.RemoteAddr())
ReceiveMsg(tf.Conn, tf.Buff) //这里的conn连接传入 LoginRequest方法的连接也行,传入Transfer的conn连接也行; 这里传入Transfer 的Buff切片是为了简洁,这样就不需要在ReceiveMsg中重新定义byte切片了,统一使用1024字节长度的字节切片(该切片使用工厂模式实例化好了,make了1024个字节)
fmt.Println("等待新用户连接...")
}
}
func ReceiveMsg(conn net.Conn, byteSlice []byte) { //持续接收消息函数(和服务端保持通讯)
for {
n, err := conn.Read(byteSlice) //从连接中读取数据到字节切片中
if err != nil {
if err == io.EOF { //表示文件结束的错误(已经读取到了文件末尾)
fmt.Printf("客户端 %v 已中断连接\n", conn.RemoteAddr())
break //这里对方连接中断会触发 io.EOF,表示通讯结束
}
fmt.Println("userProcess_server.go ->> ReceiveMsg(...) ->> conn.Read(byteSlice) err:", err)
return
}
fmt.Printf("%v -> %v", conn.RemoteAddr(), string(byteSlice[:n])) //这里Printf 不使用换行符 "\n", 是因为客户端的 "ReadString('\n')" 命令发送数据时,已经带了换行符 "\n", 所以再加一次换行符是多余的
fmt.Printf("本次接收 %v 字节\n", n)
}
}
func GetUserById(userName, userPwd string) (status int, errResult string, err error) {
myHash, err := Rdb.HGet(ctx, "users", userName).Result()
if err != nil {
if err == redis.Nil { //如果返回的错误为Nil,说明查询的键或字段在库中不存在(用户名不存在)
return 0, ErrUserNotExist, nil
}
fmt.Println("userDao.go ->> GetUserById(...) ->> Rdb.HGet(...) err")
return //如果是其他错误,打印错误,直接返回结果和错误
}
var account common.LoginMes
err = json.Unmarshal([]byte(myHash), &account)
if err != nil {
fmt.Println("userDao.go ->> GetUserById(...) ->> json.Unmarshal(...) err")
return
}
//这里完成登录密码验证(如果用户id和pwd都正确则返回user实例,否则返回错误)
if account.UserPwd == userPwd {
return 1, "", nil //如果用户输入的密码和数据库的密码一致,则把正确结果返回(可省略参数)
} else {
return 0, ErrUserPwd, nil //否则返回用户密码错误
}
}