开发者学堂课程【Go 语言核心编程 - 面向对象、文件、单元测试、反射、TCP 编程:海量用户通讯系统-登录(指定用户)】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/626/detail/9802
海量用户通讯系统-登录(指定用户)
内容介绍
一、 思路引入
二、 编写 ServerProcessMes 代码
三、 代码验证
四、 总结
一、思路引入
1、目前距离功能还差一点,现在已经完成到服务器端接收到消息,并且反序列化成对应的消息结构体。然后,服务器根据反序列化成对应的消息,判断是否登录用户合法,并返回 LeginResMes。
这一步又涉及到了新问题。服务器端能够去处理登录的消息,那就意味着将来服务器端还会处理各种其他消息,所以在这一部分,还得想办法做一个结构。也就是说不能只处理登录的,其他消息不处理。
那么此时,产生一个新的思路,目前是单一的,这个来了过后,直接开了一个协程去做事。
但是,将来这个协程也可能去处理很多事,所以这里面应该还有一种分支,就是根据实际情况,应该有个根据不同的情况去调用不同的这个函数的一个事情。那么在这里面有一个类似于这样的东西:针对协程这个地方,要考虑一下,根据它的不同的请求(处理不同的消息),也就是说在协程里边儿,应该有一种功能,或者有一种函数机制,能够让它去调用不同东西。
2、思路如下:
在服务器这里让它去调一个自己写的函数,那相对说把 message 填好过后,让 message 去调用一个函数。这个函数的名字姑且把它叫做 ServerProcesMes().. (在()里填东西)。在这里,有一个判断,就是根据消息种类不同,调用不同的函数。因为不可能处理所有消息都是一种业务逻辑,登录有登录的业务逻辑,注册有注册的业务逻辑,不可能把所有的处理都放在一个函数里面。因此存在各种函数。
接下来得思考如何将来这些函数管理起来
比如,有一个管理登录的函数(处理登录),同时之后肯定有处理注册的。有可能还会衍生出去处理发消息的,处理离线留言的,处理群发消息的等。所以会存在各种不同的函数。
首先,先对处理登录写函数,姑且对函数叫做 ServerprocessLogin
大致的意思是要在这里添加两个函数,一个是 ServerProcess Message,一个是 ServerProcessLogin ,因为在这里面,会根据它不同的情况调用不同的函数来处理,就是根据不同的消息包来调用不同的函数,最后反馈结果,然后协程根据不同的结果返回给客户端,以上就是这样一个逻辑。
补充:在编程中,尤其是程序的结构,它有一个核心的观点,优化效率一般用缓存来解决。优化程序结构,一般用分层模式来解决,这是一个非常重要的两个核心的一个观点,不管怎么样都无法脱离。要优化速度,一定是缓存加算法来解决,要优化程序的结构,一定是分层,MVC也好,其他模式也好,根本的核心思想就是各司其职,一层一层分散下去。
二、编写 ServerProcessMes 代码
1、找到服务器端,chatroom 里的 server 的 main.go(先写在一个文件内之后再进行拆分)。
2、开始编写
(1)//编写一个 ServerProcessMes 函数
//功能:根据客户端发送消息种类不同,决定调用哪个函数来处理
Func serverProcessMes(conn net.Conn,mes *message.Message) (err error){
(补充说明 1、处理东西,要给链接:conn net.Conn,2、赋予已得到的message:mes *message.Message,3、最后产生错误,把error返回)
Swich mes.Type{
(补充说明:利用 swich 能更好地根据不同的消息类型调用不同的函数。)
case message.LoginMesType:
//处理登录逻辑
(拓展:编写注册逻辑。在 message.go 中:
Type RegisterMes struct{
//…
}
给它一个类型:
LoginMesType =”LoginMes”
LoginResMesType =”LoginResMes”
RegisterMesType =” RegisterMes”
想要注册,接着写回上一段程序)
Case message.RegisterMesType:
//处理注册
default:
fm.println(“消息类型不存在,无法处理…”)
}
return
(2)正式代码
//编写一个函数 serverProcessLogin 函数,专门处理登录请求
Func serverProcessLogin(conn net.Conn,mes *message.Message) (err error)
接着是调用 serverProcessLogin,如何调用呢?
把 connect,message 传给serverProcessLogin,即serverProcessLogin(conn,mes),并返回命名的error
err=serverProcessLogin(conn,mes)
func serverProcessMes(conn net.Conn,mes*message.Message)(err error{
switch mes.Type{
case message.LoginMesType:
//处理登录登录
err=serverProcessLogin(conn,mes)
(3)核心代码
//编写一个函数serverProcessLogin函数,专门处理登录请求
func serverProcessMes(conn net.Conn,mes*message.Message)(err error{
//核心代码…
如何编写核心代码?从 serverProcessLogin 的 mes 中取出。现在已经拿到了Message【struct】,
但是data message(Login Mes)还没有取出来,要做的是把它的ID取出来,再组装一个新的message返回,就可以完成。
开始编写:
//1.先从mes中取出 mes.Data,并直接反序列化成LoginMes
Var loginMes message.LoginMes
Json.Unmarshal(mes.Data)
由于data为字符串,改成切片会更好。
续写:
Var loginMes message.LoginMes
Json.Unmarshal(【】byte(mes.Data), &LoginMes)
(经过以上处理,LoginMes被成功取出,因为可能存在错误,接收一下error即可)
Error= Json.Unmarshal(【】byte(mes.Data), &LoginMes)
if err !=nil{
fmt.Println(“Json.Unmarshal fail err=”,err)
return
}
表示反序列化失败。
如果反序列化成功,就能够进行判断。
//如果用户的id=100,密码=123456,认为合法的,否则不合法
If loginMes.UserId=100&&loginMes.UserPwd=”123456”{
//合法
} else {
//不合法
(以上代码为不为真,要有到数据库验证的逻辑,此时还未进行)
到这一步必须要做一件事情,因为如果是合法不合法,现在就可以构建一个叫做 LoginResMes,打回去对方接受一下,再解包就行了。但是为什么会这么麻烦呢?因为我们现在是写的最底层的TCP这种协议的,如果整个逻辑是用外部来开发,其实会非常简单,因为外部开发把这些工作全部做完,由框架自己全部做完。比如这边填一个用户名儿,再填一个码,一提交,对方就直接拿到一个表单了,这个表单里面把什么都取出来了,但是是框架帮你做。现在相当于很孤独,这代码全由自己完成,所以写的代码复杂度就会更高。同时这也给程序员的一个什么好处:自由度高。就底层抛给程序员,交由程序员完成,这就是为什么写c语言,c++的会更底层一些的原因。
那么,编写如下:
//先声明一个resMes
Var resMes message.Message
resMes.Type=message.LoginResMesType
//再声明一个LoginResMes
Var loginResMes message.LoginResMesType
其中,数据类型一共有两个
type LoginResMes struct{
Code int ‘json:”code”
’ //返回状态码 500表示该用户未注册,200表示登录成功
Error string ‘json:”error”’
//返回错误信息
}
现在,code和error都能确定下来,返回代码之中,
code怎么写:如果一旦成功,就给一个code码,用200,表示登录成功。不合法,说明这个用户可能还不存在,你可以注册,用500表示一个用户不合法,或者说用户不存在。
//如果用户的id=100,密码=123456,认为合法的,否则不合法
If loginMes.UserId=100&&loginMes.UserPwd=”123456”{
//合法
loginResMes.Code=200
} else {
//不合法
loginResMes.Code=500
//500状态码表示该用户不存在
}
如果还想把error带进去,因为默认是字符串,如果失败可以带一个信息给服务器端,有时候除了码之外,还有一个描述,所以可以给一个Error信息描述,如下所示。
//如果用户的id=100,密码=123456,认为合法的,否则不合法
If loginMes.UserId=100&&loginMes.UserPwd=”123456”{
//合法
loginResMes.Code=200
} else {
//不合法
loginResMes.Code=500 //500状态码表示该用户不存在
loginResMes.Error=”该用户不存在,请注册再使用…”
}