开发者学堂课程【Go 语言核心编程 - 面向对象、文件、单元测试、反射、TCP 编程:海量用户通讯系统-项目小结】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/626/detail/9809
海量用户通讯系统-用户登录(1)
目录:
一、用户登录流程
二、代码实现
一、用户登录流程
如输入的用户名密码在 Redis 中存在则登录,否则退出系统,并给出相应的提示信息:
1.用户不存在,你也可以重新注册,再登录
2.你密码不正确。
//第二步,输入的用户名和密码正确。如果输入的用户名和密码,在redis中,则存在就登陆成功,否则就退出系统,并给出相应的提示什么信息。一种是用户,不存在或者密码错误。一种是再给提示,可以重新注册再登录。
然后,可以给出提示相应的信息就是用户不存在。
可以分成两种形式提示,比如第一种是用户不存在,用户不存在一种还一种可能性,就是密码写错了。分开提示也可以,比如说用户不存在,直接提示这句话。
用户不存在,重新注册再登录,还有一种就是你的密码不正确。
可以这样提示。
代码实现,具体的步骤
代码实现建立在程序的分析基础之上,
程序的分析基础是按照刚才这个设计,一步一步往里写,打开这个幻灯片,看一下程序的结构,就是新增3个文件,然后,redis生成。先把model三个文件写完。然后,再去选redis。这个Redis,属于初始化的信息,这个文件可以直接放在文件夹下面。文件位置,可以可以根据自己的实际需要,放在相应的位置。
打开它,然后,找到server基层,别别拿错。然后model现在什么都没有。
第一个,新增加一个文件叫user.go,第二个文件,是用来专门自定义错误的error.go。第三个文件就是专门用来操作user结构体的userDao.go。
pkgLen . uint32(1en(data))
var buf [4]byte
binary.B1gEnd an.PutUint32(buf[0:4), pkgLen)
//发送长度
n, err :. conn.Nrite(buf[:4])
ifn!=4 || err!=nil{
fmt.Println("conn.write(bytes) fail", err)
return
}
//fmt. Printf("客服端,发送消息的程度=%d 内容=%s", len(data), string(data))
//发送消息本身
err . conn.Mrite(dat)
If err l-nil l
fmt.Println("conn.rite(data) fall", err)
return
}
//休眠20
//time. sleep(20 . time.Second)
//fmt.Println("休眠了20..")
//这里还需要处理服务器端返回的消息。
//创建一个Transfer实例
tf := &utils. Transfer{
Conn : conn,
}
mes, err = tf.Readpkg()
// mes就是
在Redis手动添加测试用户,并画图+说明注意后面通过程序注册用户)
流程图
main.go
1.监听
2.等待客户端的连接
3.初始化的工作
utils.go
1.把一些常用的工具的函
数,结构体
2. 提供常用的方法和函数
1.processor.go(总的处理器)
(1)根据客户端的请求,调用对应的处理器,完成相应的任务
2.smsProcess.go
(1)处理和短消息相关的请求
(2)群聊,
(3)点对点聊天
3.userProcess.go
(1)处理和用户相关的请求.
(2)登录
(3)注册
(4)注销
(5)用户列表管理
4.model[数据]
user.go
(1)定义一个User结构体
userDao.go
(1)dao: data accessobject
(2)编写对User对象(实例)操作的各种方法。主要就是增删改查.
error.go
(1)自定义错误
Ridis的连接池redis.go
Redis
use----》
100
用户信息str
200
用户信息str
二、代码实现
User.go
package model
//定义一个用户的结构体
type User struct {
//用户的结构体,用type User struct {,先确定字段。
确定字段信息,第一个就是user ID。user ID是一个int类型
第二个是用户的密码。密码是一个字符串类型。
第三个使用户的名字,是一个字符串类型。
如果这样写user ID结构体的问题就很严重了,为什么这么说?可以看到序列化的user是小写的。序列化不能成功,因此将来user一定会被反序列化或者序列化操作,因此,要进行一些代码的修改。
//确定字段信息
//为了序列化和反序列化成功,我们必须保证
//用户信息的json字符串的key 和 结构体的字段对应的 tag 名字一致!!!
UserId int `json:"userId"
UserPwd string “json:"userPwd"
UserName string “json:"userName""
//service服务层一般来说都会有的,对于一些比较复杂的项目都会有一个service层。但是因为项目较简单,这个service就没有用了。
一个标准的一个MVC,应该是中间还有一层,但是项目太小了,在标准的一个结构里,控制去掉,一般会去掉service层。
Service,再去掉model的一般四种结构。可以留一个接口Service,Service先暂时不用,因为功能很简单。
什么时候会有service?业务很复杂,service之间会相互调用,或者是service的一个业务会调用的多个dao,来完成之后,才会有service。不然的话,实际上service简单的情况下,一个到就可以处理完了。
Error.go
package model
Import (
"errors"
)
//error.go就是将来在登录的时候或者注册的时候有各种错误。有各种错误,比如说该用户已经存在了,这是一种错误,还有一种,比如说用户不存在使用错误或还用用户存在一些这种错误。打个比方,将来注册,注册这个ID已经存在了,它也是一种错误,得把这个用户ID已存在给输出出来。
第二步,error.go的代码实现
//业务逻辑的需要,需要制定自定义一些错误,完全由完全由程序员来控制,程序员想定义哪些错误,就写错误。
先定义变量,常量先这样做,要做成全局的。需要一些包。
我要一些帮帮。
首先输入"errors"
定义第一个就是用户还没有存在。
第二个是用户已存在,还有个密码,无效的密码
//根据业务逻辑需要,自定义一些错误,
var
ERROR_ USER _NOTEXISTS = errors.New("用户不存在..”) ERROR_USER_EXISTS =errors.New("用户已经存在...")
ERROR USER PWD =erros.New("密码不正确")
)
//就是说有时候这个存在一种错误,打个比方,在注册的时候。只停了一个ID,这个ID人家已经占用了。要注册一个200号的ID没有。所以说这个也有可能输入error,那么这个时候会返回一个信息,就是用户已经存在。
//那还有一种错误,比如说你输的密码不正确。
//user的密码错误了
//ERROR USER PWD =erros.New("密码不正确")就是说密码无效或者密码不正确。
//保存一下,再看这个ueser里面还有错误信息。
//Package model。
//userdao.go分析出来,提供什么样的方法,分析一下。写个包包叫model。
//说明一下,User就是定义一个userdao这么一个结构体。
看完任务是发现要完成user结构体的各种操作。
比如说,一个添加,或者一个登陆,或者一个注册,可以简单写到这里来。先定一个结构体。 Type Userdao 把它组成一个结构体,里面需要信息。因为user 最终需要去操作redis,既然要去操作这个redis,那应该至少有一个字段,能够很轻松的拿到这个连接池。
//把这个连接词作为一个字段。
Userprocess.ae ant user.go error.go userDao.go
package main import(
"fmt"
"github.com/garyburd/redigo/redis
//定义一个全局的pool
var *redis.Pool
//当启动程序时,就初始化连接池 func init(){
pool = &redis.Pool{
MaxIdle:8,
//最大空闲链接数
MaxActive:e,
//表示和数据库的最大链接数, 表示没有限制 IdleTimeout:100,
// 最大空闲时间
Dial:func()(redis.Conn,error){
// 初始化链接的代码,链接哪个ip的red return redis.Dial("tcp","localhost;6379")},
func main(){
//先从pool 取出一个链接 conn := pool.Get()
//有一个字段叫pool。Pool的类型redis是redis.Pool包,这里面有个要一个redis的一个包里面,在开发这个redis的时候,连接词的使用很重要。
//先定一个全局的pool,把pool放在那个redis.go里面比较合理,把这个路径线引进来,需要用到的redis的第三方插件,保存一下。开始写方法,在这个redis就是这个道理,第一个是完成一个登录,首先肯定会有一个登录的方法,把拿到的用户ID和密码输入,将会输出这个用户存在还是不存在,或者是有什么错误。第二个至少还要应该有一个能力,传一个ID,能不能返回一个用户的这个实例,如果没有,返回个error,这两种方法是必须的。
第一个方法,根据一个用户ID返回,返回一个user的实例加error。如果没有这个,就会返过来一个error。
//Connect理论上需要连接,这个连接有可能这方也可以拿到,也是别人传的都有可能,把这个链接拿到connect。
useDaogo
package model
import(
"fmt"
"github.com/garyburd/redigo/redis" /"encoding/json")
//定义一个UserDao 结构体体//完成对User 结构体的各种操作。
type UserDao struct {
pool*redis.Pool
//在登录的时候,从那个登录函数里去调查,最后要返回两个信息一个就是user的反馈,一个是user类型,来一个引用也可以。
//就是我们假设有了接口,这个连接有ID了就是通过给定的ID去redis里面去查询用户。操作的时候,有了连接,其实就已经可以做事情了。
//从redis获取了一个pool,获取了以前最原始的时候,操作redis里面拿一个连接,是拨号拿到的,这个类型,应该是redis里面的跟网络连接是不一样的,现在有这个连接过后,调查的一个方法叫do。
//思考一下在UserDao 应该提供哪些方法给我们
//1.根据用户id 返回一个User实例+err
func (this *UserDao) getUserbyidiconn red1s.conn
dint fuser tUse
//通过给定id 去 redis查询这个用户
res, err i= redis.string(conn.Do("HGet", "users", id)) if err != nil
//错误!
if err == redis.ErrNil {
//表示在 users 哈希中,没有找到对应id
err = ERROR USER NOTEXISTS}
return
user = &User{))
//这里我们需要把res 反序列化成User实例
err =json.Unmarshal([]byte(res),user) if err != nil {
fmt.Println("json.Unmarshal err=", err) return
return