和redis的连接

如果输入的用户名密码在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

你可能感兴趣的:(和redis的连接)