1.实现功能-完成用户登录

在redis手动添加测试用户,并画出示意图以及说明注意事项(后续通过程序注册用户)

如:输入用户名和密码,如果在redis中存在并正确,则登录,否则退出系统,并给出相应提示:

提示信息:

1.用户不存在或者密码错误

2.重新注册并登录

 redis手动添加测试用户

 

server/model/user.go 

package model//定义一个用户的结构体
type User struct {//确定字段信息//为了序列化和反序列化成功,要保证用户信息的json字符串的key和结构体对应的tag名字一致UserId int `json:"userId"`UserPwd string `json:"userPwd"`UserName string `json:"userName"`
}

server/model/userDao.go

package modelimport("fmt""github.com/garyburd/redigo/redis" //引入redis包"encoding/json"
)//希望在服务器启动后,就初始化一个userDao实例
//把它做成一个全局的变量,在需要和redis操作时,就直接使用即可
var (MyUserDao *UserDao
)//定义一个UserDao结构体,完成对User结构体的各种操作
type UserDao struct {pool *redis.Pool
}//使用工厂模式创建一个UserDao实例
func NewUserDao(pool *redis.Pool) (userDao *UserDao)  {userDao = &UserDao{pool: pool,}return
}//应该提供的方法有:
//1.根据用户id返回一个User的实例+err
func (this *UserDao) getUserById(conn redis.Conn, id int) (user *User, err error)  {//通过给定的id去redis查询res, err := redis.String(conn.Do("HGet", "users", id))if err != nil {if err == redis.ErrNil { // 表示在users这个哈希中,没有找到对应的iderr = ERROE_USER_NOTEXISTS}return}//这里需要把res反序列化成User实例user = &User{}err = json.Unmarshal([]byte(res), user)if err != nil {fmt.Println("json.UmMarshal fail, err=", err)return}return
}//完成登录校验
//1.完成对用户的验证
//2.如果用户的id,pwd正确,则返回一个user实例
//3.如果用户的id,pwd错误,则返回对应的错误信息
func (this *UserDao) Login(userId int, userPwd string) (user *User, err error) {//先从UserDao的连接池中取出一条连接conn := this.pool.Get()defer conn.Close()user, err = this.getUserById(conn, userId)if err != nil {return}//证明用户获取到了if user.UserPwd != userPwd {err = ERROE_USER_PWDreturn}return
}

server/model/error.go

package modelimport ("errors"
)
//根据业务逻辑的需要,自定义一些错误
var (ERROE_USER_NOTEXISTS = errors.New("用户不存在")ERROE_USER_EXISTS = errors.New("用户已存在")ERROE_USER_PWD = errors.New("密码不正确")ERROE_SERVER = errors.New("服务器内部错误")
)

server/main/redis.go

package mainimport("github.com/garyburd/redigo/redis" //引入redis包"time"
)//定义一个全局变量
var pool *redis.Pool//当启动程序时,就初始化连接池
func initPool(address string, maxIdle, maxActive int, idleTimeout time.Duration)  {pool = &redis.Pool{MaxIdle: maxIdle, //最大空闲连接数MaxActive: maxActive, //表示和数据库的最大连接数, 0 表示没有限制IdleTimeout: idleTimeout, //最大空闲时间Dial: func () (redis.Conn, error)  { //初始化连接的代码, 连接协议,连接哪个ipreturn redis.Dial("tcp", address)},}
}

server/main/main.go修改

package mainimport ("fmt""net""time""go_code/chatroom/server/model"
)//处理和客户端通讯
func process(conn net.Conn)  {//这里需要延时关闭defer conn.Close()//这里调用总控,创建一个总控实例processor := &Processor{Conn: conn,}err := processor.ProcessMain()if err != nil {fmt.Printf("客户端和服务器端的协程出问题了,err= %v\n", err)return}
}func init() {//当服务器启动时,就初始化redis连接池initPool("127.0.0.1:6379", 16, 0, 300 * time.Second)initUserDao()
}//编写一个函数,完成对UserDao的初始化任务
func initUserDao()  {//pool 就是一个全局的变量//这里需要注意初始化顺序问题: 先initPool(),再initUserDao()model.MyUserDao = model.NewUserDao(pool)
}func main()  {//提示信息fmt.Println("服务器[新结构]正在监听8889端口...")listen, err := net.Listen("tcp", "127.0.0.1:8889")//这里需要延时关闭defer listen.Close()if err != nil {fmt.Printf("net listen err = %v\n", err)return}//一旦监听成功,就等待客户端来连接服务器for {fmt.Println("等待客户端来连接服务器...")conn, err := listen.Accept()if err != nil {fmt.Printf("listen accept err = %v\n", err)return}//一旦连接成功,则启动一个协程,保持和客户端通讯go process(conn)}
}

server/processBlock/userProcess.go修改

package processBlockimport ("fmt""net""go_code/chatroom/common/message""go_code/chatroom/server/utils""encoding/json""go_code/chatroom/server/model"
)type UserProcess struct {Conn net.Conn
}//编写一个函数serverProcessLogin函数,专门处理登录请求
func (this *UserProcess) ServerProcessLogin(mes *message.Message) (err error)  {//先从mes中取出mes.Data,并直接反序列化成LoginMesvar loginMes message.LoginMeserr = json.Unmarshal([]byte(mes.Data), &loginMes)if err != nil {fmt.Printf("json.Unmarshal fail, err = %v\n", err)return}//1.声明一个resMesvar 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  //500 表示不存在//  loginResMes.Error = "该用户不存在,请注册后使用"// }//需要到redis数据去验证//1.使用mode.MyUserDao到redis去验证user, err := model.MyUserDao.Login(loginMes.UserId, loginMes.UserPwd)if err != nil {if err == model.ERROE_USER_NOTEXISTS {loginResMes.Code = 500} else if err == model.ERROE_USER_PWD {loginResMes.Code = 403} else {loginResMes.Code = 505}loginResMes.Error = err.Error()} else {loginResMes.Code = 200fmt.Println("user = ", user)}//3.将loginResMes序列化data, err := json.Marshal(loginResMes)if err != nil {fmt.Printf("json.Marshal fail, err = %v\n", err)return}//4.将data赋值给resMesresMes.Data = string(data)//5.对resMes序列化,准备发送data, err = json.Marshal(resMes)if err != nil {fmt.Printf("json.Marshal fail, err = %v\n", err)return}//6.发送data,将其封装到writePkg函数中//因为使用了分层模式(mvc),先创建Transfer实例,然后读取tf := &utils.Transfer{Conn: this.Conn,}err = tf.WritePkg(data)return
}   

2.实现功能-完成用户注册

(1).完成注册功能,将用户信息录入到redis中

(2).思路分析,代码展示

(1).新增 common/message/user.go

package message//定义一个用户的结构体
type User struct {//确定字段信息//为了序列化和反序列化成功,要保证用户信息的json字符串的key和结构体对应的tag名字一致UserId int `json:"userId"`UserPwd string `json:"userPwd"`UserName string `json:"userName"`
}

(2). common/message/message.go新增了方法

package message//定义消息类型
const (LoginMesType = "LoginMes"LoginResMesType = "LoginResMes"RegisterMesType = "RegisterMes"RegisterResMesType = "RegisterResMes"
)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"` //返回状态码: 200 登录成功, 500 用户未注册Error string `json:"error"` //返回错误信息
}type RegisterMes struct {User User `json:"user"` //类型就是User结构体
}type RegisterResMes struct {Code int `json:"code"` //返回状态码: 200 注册成功, 40 用户已被占用Error string `json:"error"` //返回错误信息
}

(3).client/processBlock/userProcess.go新增方法

func (this *UserProcess) Register(userId int, userPwd, userName string) (err error){//1.连接到服务器端conn, err := net.Dial("tcp", "127.0.0.1:8889")if err != nil {fmt.Printf("net dial err = %v\n", err)return}//延时关闭defer conn.Close()  //2.准备通过conn发送消息给服务端var mes message.Messagemes.Type = message.RegisterMesType//3.创建一个RegisterMes结构体var registerMes message.RegisterMesregisterMes.User.UserId = userIdregisterMes.User.UserPwd = userPwdregisterMes.User.UserName = userName//4.将registerMes序列化data, err := json.Marshal(registerMes)if err != nil {fmt.Printf("json marshal err = %v\n", err)return}//5.将序列化后的registerMes byte切片赋给mes.Datames.Data = string(data)//6.将mes序列化data, err = json.Marshal(mes)if err != nil {fmt.Printf("json marshal err = %v\n", err)return}//创建一个Transfer实例tf := &utils.Transfer{Conn: conn,}//发送data给服务器err = tf.WritePkg(data)if err != nil {fmt.Println("WritePkg(data) err = ", err)return}//读取返回的消息mes, err = tf.ReadPkg() //mes就是RegisterResMesif err != nil {fmt.Println("readPkg(conn) err = ", err)return}//将mes中的Data反序列化成RegisterResMesvar registerResMes message.RegisterResMeserr = json.Unmarshal([]byte(mes.Data), &registerResMes)if err != nil {fmt.Println("json.Unmarshal err = ", err)return}if registerResMes.Code == 200 {fmt.Println("注册成功,请重新登录") os.Exit(0)} else {fmt.Println(registerResMes.Error)os.Exit(0)}return
}

(4).client/main/main.go完善了注册请求

for true {fmt.Println("-------------------欢迎登录多人聊天系统-------------------")fmt.Println("\t\t\t\t 1 登录聊天室")fmt.Println("\t\t\t\t 2 注册用户")fmt.Println("\t\t\t\t 3 退出系统")fmt.Println("\t\t\t\t 请选择(1~3):")fmt.Scanf("%d\n", &key)switch key {case 1 : fmt.Println("登录聊天室")//说明用户要登录fmt.Println("请输入用户的id:")fmt.Scanf("%d\n", &userId)fmt.Println("请输入用户的密码:")fmt.Scanf("%s\n", &userPwd)//先把登录的函数写到另一个文件,比如:login.go//因为使用了分层模式(mvc),故调用分层模式中processBlock.UserProcess.Login()处理up := &processBlock.UserProcess{}up.Login(userId, userPwd)case 2 : fmt.Println("注册用户")fmt.Println("请输入用户的id:")fmt.Scanf("%d\n", &userId)fmt.Println("请输入用户的密码:")fmt.Scanf("%s\n", &userPwd)fmt.Println("请输入用户名:")fmt.Scanf("%s\n", &userName)//因为使用了分层模式(mvc),故调用分层模式中processBlock.UserProcess.Register()处理注册相关逻辑up := &processBlock.UserProcess{}up.Register(userId, userPwd, userName)case 3 :  fmt.Println("退出系统")os.Exit(0)default:fmt.Println("输入有误,请重新输入")}}

(5).server/model/userDao.go增加了方法

//完成注册
func (this *UserDao) Register(user *message.User) (err error) {//先从UserDao的连接池中取出一条连接conn := this.pool.Get()defer conn.Close()_, err = this.getUserById(conn, user.UserId)if err == nil {err = ERROE_USER_EXISTSreturn}//说明id在redis中还没有,则可以完成注册data, err := json.Marshal(user) //序列化if err != nil {return}//入库_, err = conn.Do("HSet", "users", user.UserId, string(data))if err != nil {fmt.Println("保存注册用户错误,err=", err)return}return
}

(6).server/processBlock/userProcess.go增加注册方法

//编写一个函数ServerProcessRegister函数,专门处理注册请求
func (this * UserProcess) ServerProcessRegister (mes *message.Message) (err error)  {//先从mes中取出mes.Data,并直接反序列化成RegisterMesvar registerMes message.RegisterMeserr = json.Unmarshal([]byte(mes.Data), &registerMes)if err != nil {fmt.Printf("json.Unmarshal fail, err = %v\n", err)return}//1.声明一个resMesvar resMes message.MessageresMes.Type = message.RegisterResMesType//2.再声明一个registerResMes,并完成赋值var registerResMes message.RegisterResMes//需要到redis数据去验证,完成注册//1.使用mode.MyUserDao到redis去验证err = model.MyUserDao.Register(&registerMes.User)if err != nil {if err == model.ERROE_USER_EXISTS {registerResMes.Code = 505} else {registerResMes.Code = 404}registerResMes.Error = err.Error()}//3.将registerResMes列化data, err := json.Marshal(registerResMes)if err != nil {fmt.Printf("json.Marshal fail, err = %v\n", err)return}//4.将data赋值给resMesresMes.Data = string(data)//5.对resMes序列化,准备发送data, err = json.Marshal(resMes)if err != nil {fmt.Printf("json.Marshal fail, err = %v\n", err)return}//6.发送data,将其封装到writePkg函数中//因为使用了分层模式(mvc),先创建Transfer实例,然后读取tf := &utils.Transfer{Conn: this.Conn,}err = tf.WritePkg(data)return
}

(7).server/main/processor.go修改了调用方法

//编写一个ServerProcessMes函数
//功能:根据客户端发送消息类型不同,决定调用哪个函数来处理
func (this *Processor) serverProcessMes(mes *message.Message) (err error)  {switch mes.Type {case message.LoginMesType : //处理登录消息//创建一个UserProcessup := &processBlock.UserProcess{Conn: this.Conn,}err = up.ServerProcessLogin(mes)case message.RegisterMesType : //处理注册up := &processBlock.UserProcess{Conn: this.Conn,}err = up.ServerProcessRegister(mes)default :fmt.Println("消息类型不存在, 无法处理...")}return
}

[上一节][go学习笔记.第十六章.TCP编程] 2.项目-海量用户即时通讯系统

[下一节] [go学习笔记.第十六章.TCP编程] 4.项目-海量用户即时通讯系统-显示在线用户列表,群聊

[go学习笔记.第十六章.TCP编程] 3.项目-海量用户即时通讯系统-redis介入,用户登录,注册相关推荐

  1. [汇编学习笔记][第十六章直接定址表]

    第十六章 直接定址表 16.1 描述了单元长度的标号 格式 code segmenta db 1,2,3,4,5,6,7,8,b dw 0 功能 此时标号a,b 不仅代表了内存单元,还代表了内存长度 ...

  2. C++ Primer plus学习笔记-第十六章:string类和标准模板库

    第十六章:string类和标准模板库 前言:这一章已经相当靠近全书的后面部分了:这一章我们会深入探讨一些技术上的细节,比如string的具体构造函数,比如适用于string类的几个函数,比如我们还会介 ...

  3. 深入立即Linux网络技术内幕学习笔记第十六章:桥接:Linux实现

    网桥设备抽象: 对Linux而言,网桥是虚拟设备,要想传输或接收数据,需要将真实设备绑定到虚拟网桥上. 上图中,有几点需要注意: LAN1和LAN2通过网桥连接在一起,子网都是一样的. 网桥连接到路由 ...

  4. Java学习笔记(十六)—— 开发个小项目(GoBang4.0)

    接十二,今天重点搞定简单基础版本的AI下棋. 画个大纲(跟随慢慢开发过程不断完善) 1.用户 两个用户对战  一黑一白 用户可以是人,也可以是AI.对战模式支持人人,人机,机机. 属性 本次比赛执棋颜 ...

  5. TCP/IP详解 卷1:协议 学习笔记 第十六章 BOOTP:引导程序协议

    一个无盘系统在不知道自身IP地址情况下,进行系统引导时能通过RARP协议获取它的IP地址,使用RARP会有两个问题:(1)IP地址是返回的唯一结果:(2)RARP使用链路层广播,RARP请求不会被路由 ...

  6. UNIX环境高级编程 学习笔记 第十六章 网络IPC:套接字

    socket的设计目标之一:同样的接口既可以用于计算机间通信,也可以用于计算机内通信.socket接口可采用许多不同的网络协议进行通信,本章讨论限制在因特网事实上的通信标准:TCP/IP协议栈. 套接 ...

  7. Linux shell编程学习笔记-----第十六章

    shell 脚本调试技术:trap命令 tee命令 调试钩子  和shell选项.前三者都需要修改shell脚本的源代码,后者不需要. trap 命令用于捕捉信号,shell脚本在执行时,会产生三个所 ...

  8. 《VC++深入详解》学习笔记 第十六章 线程同步与异步套接字编程

    (颠簸喜悲幽若尽是无情人) 事件对象成员: 包含(使用计数.事件类型.事件状态) 事件类型: 人工重置的事件对象:得到通知时,等待的所有线程都变为可调度     自动重置的事件对象:得到通知时,等待的 ...

  9. C++ Primer学习笔记-----第十六章:模板与泛型编程

    模板是C++中泛型编程的基础. 模板是蓝图,用来创建类型,创建的类型就是模板的实例,就好像我们用一个类型创建相应的实例一样. 函数模板 template<typename T> //模板参 ...

最新文章

  1. 【OpenCV】使用过的函数汇总
  2. 架构设计贵在务实(转载)
  3. JS获取当天零点或23:59:59的时间
  4. 在iOS中求数组元素中最大数与最小数
  5. 2021-09-15
  6. 关于微信支付的退款那些事
  7. LeetCode_617.合并二叉树
  8. IOS-RunTime(刨根问底)
  9. 【转载】我喜欢电脑的飞鸽传书
  10. 【Spark】Spark ListenerBus 和 MetricsSystem 体系分析
  11. UI设计进阶干货|切图命名
  12. 使用API Monitor监测到目标程序对系统API函数的调用(常用分析工具)
  13. java实现户籍管理系统_基于javaweb的户籍管理系统(含配套论文等资料)
  14. Javascript 实现汉字简繁体互相转换
  15. 中学生应具备的良好的学习习惯
  16. 品牌机Windows10重置功能WinRE失效处理方法
  17. 为什么安装step7时要重启计算机,step7安装提示重启怎么解决
  18. 南安一中八十年校庆征文 陈建春老师
  19. Big sur提示您没有权限来打开应用程序“XXX”怎么办?
  20. Typora更新版本后要收费

热门文章

  1. 用C语言输出各种三角形
  2. 云南高中计算机会考成绩,云南省某校2009年12月高中信息技术会考实录
  3. 串行通信协议小结(Serial Protocols)(1)
  4. 合工大Python语言与系统设计大作业:微博评论文本情感分析
  5. 访问学者在新加坡访学有哪些饮食习惯?
  6. StageFright框架流程解读
  7. 输出信噪比公式_如何计算信号的信噪比
  8. 国际中的steam教育发展与启示
  9. 已知总线长度为1km,信号在总线上的传播速度为2×10^8m/s,数据传输速率为10Mbit/s。请问CSMA/CD算法成立的最短帧长度是多少?写出计算过程。
  10. C语言常用库函数总结