如果输入的用户名密码在redis中存在则登录,否则退出系统,并给出相应的提示
提示信息
- 用户不存在或者是密码错误
- 你也可以重新注册再登录
代码实现
代码框架package main
import (
"awesomeProject/chatroom/client/process"
"fmt"
)
//一个表示用户id 一个表示用户密码
var usrId int
var usrpwd string
func main() {
//接收用户的选择
var key int
//判断是否继续显示菜单
//var loop = true
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("登录聊天室")
//loop = false
fmt.Println("请用户输入自己的ID")
fmt.Scanf("%d\n",&usrId)
fmt.Println("请用户输入自己的密码")
fmt.Scanf("%s\n",&usrpwd)
//先把登录的函数 写到另外一个文件中 login.go
//这里我们需要从新调用
//完成登录 创建一个UserProcess的实例
up := &process.UserProcess{}
up.Login(usrId,usrpwd)
case 2:
fmt.Println("注册用户")
//loop = false
case 3:
fmt.Println("退出系统")
//loop = false
default:
fmt.Println("你的输入有误,请从新输入")
}
}
//根据用户输入显示新的提示信息
//if key == 1{
// //说明用户要登录
// fmt.Println("请用户输入自己的ID")
// fmt.Scanf("%d\n",&usrId)
// fmt.Println("请用户输入自己的密码")
// fmt.Scanf("%s\n",&usrpwd)
// //先把登录的函数 写到另外一个文件中 login.go
// //这里我们需要从新调用
//
// login(usrId,usrpwd)
// //if err!=nil {
// // fmt.Println("登录失败")
// //}else {
// // fmt.Println("登录成功")
// //}
//}else if key == 2{
// fmt.Println("进行用户注册")
//}
}
client/main/main.go
package process
import (
"awesomeProject/chatroom/client/utils"
"fmt"
"net"
"os"
)
func ShowMenu() {
fmt.Println("-------恭喜***登录成功---------")
fmt.Println("-------1 显示在线用户列表---------")
fmt.Println("-------2 发送消息---------")
fmt.Println("-------3 退出系统---------")
fmt.Println("请选择1-4")
var key int
fmt.Scanf("%d\n",&key)
switch key {
case 1:
fmt.Println("显示在线用户列表")
case 2:
fmt.Println("发送消息")
case 3:
fmt.Println("查看信息列表")
case 4:
fmt.Println("你选择退出了系统")
os.Exit(0)
default:
fmt.Println("你输入的选项不对")
}
}
//和服务器端保持通讯
func serverProcessMes(conn net.Conn) {
//创建一个Transfer 实例 不停的读取服务器发送的消息
tf := &utils.Transfer{
Conn: conn,
}
for {
fmt.Println("客户端正在等待服务器发送的消息")
mes,err := tf.ReadPkg()
if err != nil{
fmt.Println("read err=",err)
return
}
//如果读取到消息
fmt.Println(mes)
}
}
client/process/server.go
package process
import (
"awesomeProject/chatroom/client/utils"
"awesomeProject/chatroom/common/message"
"encoding/binary"
"encoding/json"
"fmt"
"net"
)
type UserProcess struct {
//暂时不需要字段。。。。
}
//给关联一个用户登录的方法
func (this *UserProcess)Login(usrId int,usrpwd string)(err error) {
////下一步要开始定协议
//fmt.Printf("userId= %d usrpwd=%s\n",usrId,usrpwd)
//return nil
conn,err := net.Dial("tcp","localhost:8889")
if err!=nil{
fmt.Println("net Dail err=",err)
return
}
//延时关闭
defer conn.Close()
//2 通过conn发送消息给服务
var mes message.Message
mes.Type = message.LoginMesType
//3 创建一个LoginMes 结构体
var LoginMes message.LoginMes
LoginMes.UserId = usrId
LoginMes.UserPwd = usrpwd
//4 将loginMes 序列化
data,err := json.Marshal(LoginMes)
if err!=nil{
fmt.Println("json marshal err=",err)
return
}
//5 把data赋给 mes.data 字段
mes.Data = string(data)
// 6 将mes 进行序列化
data,err = json.Marshal(mes)
if err!= nil{
fmt.Println("json marshal err=",err)
return
}
//7 这个时候data 就是我们要发送的数据
//7.1 先把data的长度发送给服务器
// 先获得到 data的长度-》转成一个表示长度的byte切片
var pkgLen uint32
pkgLen = uint32(len(data))
var buf [4]byte
binary.BigEndian.PutUint32(buf[0:4],pkgLen)
//发送长度
n,err := conn.Write(buf[:4])
if err!=nil||n != 4 {
fmt.Println("conn write err=",err)
return
}
fmt.Printf("客户端,发送消息长度ok 有%d 字节 %s",len(data),string(data))
// 发送消息本身
_,err = conn.Write(data)
if err != nil{
fmt.Println("conn write err=",err)
return
}
//这里还需要处理服务器端返回的消息
//创建一个Transfer 实例
tf := &utils.Transfer{
Conn: conn,
}
mes,err = tf.ReadPkg()
if err != nil{
fmt.Println("readpkg err=",err)
return
}
//将mes的data部分反序列化为 LoginResMes
var loginResMes message.LoginResMes
err = json.Unmarshal([]byte(mes.Data),&loginResMes)
if loginResMes.Code == 200 {
fmt.Println("用户登录成功")
//这里我们还需要启动一个协程
//该协程保持和服务器通讯 如果服务器有数据推送给客户端
//则接收并显示在客户端的终端
go serverProcessMes(conn)
// 1 显示登录成功的菜单 循环
for {
ShowMenu()
}
}else {
fmt.Println(loginResMes.Error)
}
return
}
client/process/userProcess.go
package utils
import (
"awesomeProject/chatroom/common/message"
"encoding/binary"
"encoding/json"
"fmt"
"net"
)
//这里将这些方法关联到结构体中
type Transfer struct {
//分析应该有哪些字段
Conn net.Conn
Buf [8096]byte // 这是传输时使用的缓存
}
func (this *Transfer)ReadPkg()(mes message.Message,err error) {
//buf := make([]byte,8096)
fmt.Println("等待客户端发送的数据")
// conn read 在conn没有被关闭的情况下,才会阻塞
//如果客户端关闭了 conn 就不会阻塞了
_,err = this.Conn.Read(this.Buf[:4])
if err != nil{
fmt.Println("conn read err=",err)
return
}
// 根据读到的buf长度 转换为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) || err != nil{
fmt.Println("conn read err=",err)
return
}
//pkgLen 反序列化成_>message.Message 的类型
err = json.Unmarshal(this.Buf[:pkgLen],&mes)
if err != nil{
fmt.Println("json 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 err!=nil||n != 4 {
fmt.Println("conn write err=",err)
return
}
//发送data 本身
n,err = this.Conn.Write(data)
if n != int(pkgLen)||err !=nil{
fmt.Println("conn write er=",err)
return
}
return
}
client/utils/utils.go
package message
const (
LoginMesType = "LoginMes"
LoginResMesType = "LoginResMes"
RegisterMesType = "RegisterMes"
)
type Message struct {
Type string `json:"type"`//消息的类型
Data string `json:"data"`//消息的内容
}
//定义两个消息
type LoginMes struct {
UserId int `json:"userid"`//用户ID
UserPwd string `json:"userpwd"`//用户密码
UserName string `json:"username"`//用户名
}
type LoginResMes struct {
Code int `json:"code"`//返回的状态码 500表示用户未注册 200表示登录成功
Error string `json:"error"`//返回错误信息
}
type RegisterMes struct {
}
common/message/message.go
package main
import (
"awesomeProject/chatroom/server/model"
"fmt"
"net"
"time"
)
//func readPkg(conn net.Conn)(mes message.Message,err error) {
// buf := make([]byte,8096)
//
// fmt.Println("等待客户端发送的数据")
// // conn read 在conn没有被关闭的情况下,才会阻塞
// //如果客户端关闭了 conn 就不会阻塞了
// _,err = conn.Read(buf[:4])
// if err != nil{
// fmt.Println("conn read err=",err)
// return
// }
// // 根据读到的buf长度 转换为uint32 的类型
// var pkgLen uint32
// pkgLen = binary.BigEndian.Uint32(buf[0:4])
// // 根据pkgLen 读取消息内容
// n,err := conn.Read(buf[:pkgLen])
// if n != int(pkgLen) || err != nil{
// fmt.Println("conn read err=",err)
// return
// }
// //pkgLen 反序列化成_>message.Message 的类型
// err = json.Unmarshal(buf[:pkgLen],&mes)
// if err != nil{
// fmt.Println("json err=",err)
// return
// }
// return
//}
//
//func writePkg(conn net.Conn,data []byte)(err error) {
// //先发送一个长度给对方
// var pkgLen uint32
// pkgLen = uint32(len(data))
// var buf [4]byte
// binary.BigEndian.PutUint32(buf[0:4],pkgLen)
// //发送长度
// n,err := conn.Write(buf[:4])
// if err!=nil||n != 4 {
// fmt.Println("conn write err=",err)
// return
// }
// //发送data 本身
// n,err = conn.Write(data)
// if n != int(pkgLen)||err !=nil{
// fmt.Println("conn write er=",err)
// return
// }
// return
//}
//编写一个函数专门处理登录请求
//func serverProcessLogin(conn net.Conn,mes *message.Message) (err error) {
// //核心代码
// //1 先从mes中取 mes.data,并这接反序列化成LoginMes
// var loginMes message.LoginMes
// err = json.Unmarshal([]byte(mes.Data),&loginMes)
// if err != nil{
// fmt.Println("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 =="123456"{
// //合法
// loginResMes.Code = 200
//
// }else {
// //不合法
// loginResMes.Code = 500 //500状态码 表示用户不存在
// loginResMes.Error = "该用户不存在,请重新注册"
// }
// // 3 将 loginResMes 进行序列化
// data ,err := json.Marshal(loginResMes)
// if err != nil{
// fmt.Println("序列化失败 err=",err)
// return
// }
// //4 将data 赋值给resMes
// resMes.Data = string(data)
//
//
// //5 对resMes进行序列化 准备发送
// data ,err = json.Marshal(resMes)
// if err != nil{
// fmt.Println("Marshal err=",err)
// return
// }
// //6 发送data 我们将其封装到writePkg 函数中去
// err = writePkg(conn,data)
// return
//}
//编写一个ServerProcessMessage 函数
//功能:根据客户端发送的消息种类不同,决定调用哪个函数来处理
//func serverProcessMessage(conn net.Conn,mes *message.Message)(err error) {
// switch mes.Type {
// case message.LoginMesType:
// //处理登录
// err = serverProcessLogin(conn,mes)
// case message.RegisterMesType:
// //处理注册
// default:
// fmt.Println("消息类型不存在无法处理")
//
// }
// return
//}
//处理和客户端 的通讯
func process(conn net.Conn) {
//这里需要延时关闭
defer conn.Close()
//读客户端发送的信息
//for {
// //这里我们将读取数据包,直接封装成一个函数readPkg(),返回Message,Err
// mes,err := readPkg(conn)
// if err != nil{
// if err == io.EOF{
// fmt.Println("客户端退出,服务器也退出了")
// return
// }else {
// fmt.Println("readpkg err =",err)
// return
// }
// }
// fmt.Println("mes = ",mes)
// err = serverProcessMessage(conn,&mes)
// if err != nil{
// return
// }
//}
//这里要调用总控 先创建一个总控实例
processor := &Processor{Conn:conn}
err := processor.process1()
if err != nil{
fmt.Println("客户端和服务器通讯协成错误=",err)
return
}
}
//这里我们编写一个函数,完成对UserDao的初始化的任务
func initUserDao() {
//这里的pool 本身就是一个全局的变量
//这里需要注意一个初始化的顺序问题 先 initpool 再进行initUserDap
model.MyUserDao = model.NewUserDao(pool)
}
func main() {
//当服务器启动时,我们就去初始化我们的redis的连接池
initPool("localhost:6379",16,0,300*time.Second)
initUserDao()
//提示信息
fmt.Println("服务器在8889端口监听")
listen,err := net.Listen("tcp","0.0.0.0:8889")
defer listen.Close()
if err!= nil {
fmt.Println("listen err = ",err)
return
}
//一旦监听成功
for {
fmt.Println("等待客户端来连接服务器")
conn,err := listen.Accept()
if err != nil{
fmt.Println("lsten accept err=",err)
}
//一旦连接成功,则启动一个协成和客户端保持通讯
go process(conn)
}
}
server/main/main.go
package main
import (
"awesomeProject/chatroom/common/message"
process2 "awesomeProject/chatroom/server/process"
"awesomeProject/chatroom/server/utils"
"fmt"
"io"
"net"
)
//先创建一个process的结构体
type Processor struct {
Conn net.Conn
}
func (this *Processor)serverProcessMessage(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.Println("消息类型不存在无法处理")
}
return
}
func (this *Processor)process1()(err error) {
for {
//这里我们将读取数据包,直接封装成一个函数readPkg(),返回Message,Err
//创建一个Transfer 实例完成读包任务
tf := &utils.Transfer{
Conn: this.Conn,
}
mes,err := tf.ReadPkg()
if err != nil{
if err == io.EOF{
fmt.Println("客户端退出,服务器也退出了")
return err
}else {
fmt.Println("readpkg err =",err)
return err
}
}
fmt.Println("mes = ",mes)
err = this.serverProcessMessage(&mes)
if err != nil{
return err
}
}
}
server/main/processor.go
package main
import (
"github.com/garyburd/redigo/redis"
"time"
)
//定义一个全局pool
var pool *redis.Pool
func initPool(adderss string,maxIdle,maxActive int,idleTimeout time.Duration) {
pool = &redis.Pool{
Dial: func() (conn redis.Conn, err error) {
return redis.Dial("tcp",adderss)
},
MaxIdle: maxIdle,
MaxActive: maxActive,
IdleTimeout: idleTimeout,
}
}
server/main/redis.go
package model
import "errors"
//根据业务逻辑需要自定义一些错误
var (
ERROR_USER_NOTEXISTS = errors.New("用户不存在。。。。")
ERROR_USER_EXISTS = errors.New("用户已经存在")
ERROR_USER_PWD = errors.New("密码不正确")
)
server/model/error.go
package model
//定义一个用户的结构体
type User struct {
//先确定字段信息
//为了序列化和反序列化成功 我们必须保证
//用户信息的json字符串的key 和 结构体的字段对应的tag 名字一致!!!! 否则会出现错误
UserId int `json:"userId"`
UserPwd string`json:"usrPwd"`
UserName string`json:"usrName"`
}
server/model/user.go
package model
import (
"encoding/json"
"fmt"
"github.com/garyburd/redigo/redis"
)
//在服务启动的时候 我们就初始化一个UserDao的实例 把它做成一个全局的变量 在需要和redis操作时,就直接使用即可
var(
MyUserDao *UserDao
)
//定义一个UserDao 的结构体
//完成对User 结构体的各种操作。
type UserDao struct {
pool *redis.Pool
}
//使用工厂模式 创建一个UserDao的实例
func NewUserDao(pool *redis.Pool)(userDoa *UserDao) {
userDoa = &UserDao{
pool:pool,
}
return
}
//思考一下在UserDao 应该提供哪些方法给我们呢
// 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哈希中 没有找打对应的ID
err = ERROR_USER_NOTEXISTS
}
return
}
//这里我们需要把 res 反序类话 user实例
user = &User{}
err = json.Unmarshal([]byte(res),user)
if err != nil{
fmt.Println("json unmarshal err =",err)
}
return
}
//完成登录校验
//Login 完成对用户的验证
//如果用户的密码和ID都正确 则返回一个user 实例
// 如果用户的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 = ERROR_USER_PWD
return
}
return
}
server/model/userDao.go
package process
import (
"awesomeProject/chatroom/common/message"
"awesomeProject/chatroom/server/model"
"awesomeProject/chatroom/server/utils"
"encoding/json"
"fmt"
"net"
)
type UserProcess struct {
Conn net.Conn
}
func (this *UserProcess)ServerProcessLogin(mes *message.Message) (err error) {
//核心代码
//1 先从mes中取 mes.data,并这接反序列化成LoginMes
var loginMes message.LoginMes
err = json.Unmarshal([]byte(mes.Data),&loginMes)
if err != nil{
fmt.Println("json Unmarshal err=",err)
return
}
//1先申明一个 resMes
var resMes message.Message
resMes.Type = message.LoginResMesType
//2在声明一个 LoginResMes 并完成赋值
var loginResMes message.LoginResMes
//我们需要到redis数据库中去完成验证
//1使用 MyUserDao到redis去验证
user,err := model.MyUserDao.Login(loginMes.UserId,loginMes.UserPwd)
if err != nil{
if err == model.ERROR_USER_NOTEXISTS{
loginResMes.Code = 500
//这里我们先测试成功,然后我们再返回具体的错误信息
loginResMes.Error = err.Error()
}else if err == model.ERROR_USER_PWD{
loginResMes.Code = 403
//这里我们先测试成功,然后我们再返回具体的错误信息
loginResMes.Error = err.Error()
}else {
loginResMes.Code = 505
//这里我们先测试成功,然后我们再返回具体的错误信息
loginResMes.Error = "服务器内部错误"
}
}else {
loginResMes.Code = 200
fmt.Println(user)
}
//如果用户id = 100 密码=123456 认为合法
if loginMes.UserId == 100 && loginMes.UserPwd =="123456"{
//合法
loginResMes.Code = 200
}else {
//不合法
loginResMes.Code = 500 //500状态码 表示用户不存在
loginResMes.Error = "该用户不存在,请重新注册"
}
// 3 将 loginResMes 进行序列化
data ,err := json.Marshal(loginResMes)
if err != nil{
fmt.Println("序列化失败 err=",err)
return
}
//4 将data 赋值给resMes
resMes.Data = string(data)
//5 对resMes进行序列化 准备发送
data ,err = json.Marshal(resMes)
if err != nil{
fmt.Println("Marshal err=",err)
return
}
//6 发送data 我们将其封装到writePkg 函数中去
//因为使用分层模式(MVC) 我们先创建一个Transfer 实例 然后读取
tf := &utils.Transfer{
Conn: this.Conn,
}
err = tf.WritePkg(data)
return
}
server/process/userProcess.go
package utils
import (
"awesomeProject/chatroom/common/message"
"encoding/binary"
"encoding/json"
"fmt"
"net"
)
//这里将这些方法关联到结构体中
type Transfer struct {
//分析应该有哪些字段
Conn net.Conn
Buf [8096]byte // 这是传输时使用的缓存
}
func (this *Transfer)ReadPkg()(mes message.Message,err error) {
//buf := make([]byte,8096)
fmt.Println("等待客户端发送的数据")
// conn read 在conn没有被关闭的情况下,才会阻塞
//如果客户端关闭了 conn 就不会阻塞了
_,err = this.Conn.Read(this.Buf[:4])
if err != nil{
fmt.Println("conn read err=",err)
return
}
// 根据读到的buf长度 转换为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) || err != nil{
fmt.Println("conn read err=",err)
return
}
//pkgLen 反序列化成_>message.Message 的类型
err = json.Unmarshal(this.Buf[:pkgLen],&mes)
if err != nil{
fmt.Println("json 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 err!=nil||n != 4 {
fmt.Println("conn write err=",err)
return
}
//发送data 本身
n,err = this.Conn.Write(data)
if n != int(pkgLen)||err !=nil{
fmt.Println("conn write er=",err)
return
}
return
}
server/utils/utils.go
网友评论