美文网首页
海量用户及时通讯系统

海量用户及时通讯系统

作者: 乔大叶_803e | 来源:发表于2020-04-11 16:33 被阅读0次

    需求分析

    • 用户注册
    • 用户登录
    • 显示用户在线的用户列表
    • 群聊
    • 点对点聊天
    • 离线留言

    界面设计

    界面设计

    项目开发前技术储备

    项目要保存用户信息和消息数据,因此我们需要学习数据库(redis或者是MySQL)这里我们选择Redis

    go中使用redis

    Redis的基本介绍

    redis是nosql型数据库 不是关系型数据库
    Redis是远程字典服务器,性能比较高,单机性能高,适合做缓存,也可以持久化

    是完全开元免费的,高性能的分布式内存数据库

    redis的操作指令

    redis的操作指令

    操作指令
    redis的基本使用
    • 添加key-val [set]
    • 获取当前redis所有key的值 [key *]
    • 获取key对应的值[get key]
    • 切换redis数据库[select index]
    • 如何查看当前数据库的key-value值[dbsize]
    • 清空当前数据库的key-val和清空所有数据库的key-val[flushdb flushall]

    Redis的五大数据类型

    Redis的五大数据类型:string hash List set(集合) 和 zset(有序集合)

    string介绍

    string是redis的最基本的类型,一个key对应一个value
    string类型是二进制安全的 除普通字符外,也可以存放图片数据

    set [如果存在就相当于修改 如果不存在就相当于增加]/get/del[删除]

    setex键秒值

    setex key seconds value

    setex

    超过10秒后就自动消失了

    mset 同时可以设置多个key val

    mset mget

    Hash(哈希,类似go中的map)

    redis hash是一个键值对集合。var user1 map[string]string

    redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。

    举例存放一个user信息

    user1 name 张三 age30
    说明:
    key:user1
    name 张三 和 age 30 是两对 field-value

    hset

    hset 增加
    hget 获取

    hgetall 获取所有的

    用这个就不用一个一个get了

    hgetall

    list 介绍

    list是简单的字符串列表,按照插入顺序排序,你可以添加一个元素到列表的头部或者是尾部

    list本质是个链表,list元素是有序的,元素的值可以重复

    list操作

    lpush

    增加

    lrange

    取出字段

    list注意事项

    • index按照索引下标获取元素(从左到右,编号从0开始)
    • LLen key
      返回当前key的长度,如果key不存在,则key被解释为一个空列表,返回0
    • List的其他说明
      list数据,可以从左或者右插入添加
      如果值全移除了,对应的键也消失了

    set集合的介绍

    Redis的set是string类型的无序集合

    底层是hashTable数据结构,set也是存放很多字符串的元素,字符串元素是无序的,而且元素的值不能重复

    举例,存放多个邮件列表信息:
    email sgg@email.com

    set

    可以看出加入后取出是无序的

    集合的其他指令

    • sadd 添加
    • smembers 取出所有值
    • sismember 判断是否是成员
    • srem 删除指定值

    golang 操作redis

    安装redis库

    • 使用第三方库redis: go get github.com/garyburd/redigo/redis
      在gopath下执行

    • 安装好有个github.com的包

    代码连接操作

    package main
    
    import (
        "fmt"
        "github.com/garyburd/redigo/redis"
    )
    
    func main()  {
        //通过go向redis 写入数据读取数据
        //  连接到redis
        conn,err := redis.Dial("tcp","127.0.0.1:6379")
        if err!=nil{
            fmt.Println("redis Dial err=",err)
            return
        }
        fmt.Println("conn succ=",conn)
    }
    
    

    通过Set、Get结构进行操作数据

    package main
    
    import (
        "fmt"
        "github.com/garyburd/redigo/redis"
    )
    
    func main()  {
        //通过go向redis 写入数据读取数据
        //  连接到redis
        conn,err := redis.Dial("tcp","127.0.0.1:6379")
        if err!=nil{
            fmt.Println("redis Dial err=",err)
            return
        }
    
        defer conn.Close()//关闭一定不能忘记
        
        //通过go向redis中添加数据  string【key-value】
        _, err = conn.Do("Set","name","tom")
        if err != nil{
            fmt.Println("set err=",err)
            return
        }
    
        //读取数据
    
        r, err := redis.String(conn.Do("Get","name"))
        if err != nil{
            fmt.Println("Get err=",err)
            return
        }
    
        //因为返回的r是interface{}
        //因为name对应的值是string  因此我们需要转换
    
    
        fmt.Println("操作成功  r=",r)
    }
    
    

    操作hash

        _, err := conn.Do("HSet","user01","name","tom")
    
        if err != nil{
            fmt.Println("set err=",err)
            return
        }
    
        r, err := redis.String(conn.Do("HGet","name"))
        if err != nil{
            fmt.Println("Get err=",err)
            return
        }
    
    

    也就是把set换成 HSet
    这样就可以了

    操作List

    核心代码

        _, err := conn.Do("Lpush","heroList","宋江","武松")
        r, err := redis.String(conn.Do("rpop","heroList"))
    

    redis 连接池问题

    • 事先初始化一定数量的连接,放入到连接池
    • 当go需要操作redis时,直接从redis连接池中取出连接就行了
    • 这样可以节省获取redis连接的时间,提高效率


      连接池

    运行前一定要初始化

    代码案例

    package main
    
    import (
        "fmt"
        "github.com/garyburd/redigo/redis"
    )
    
    //定义一个全局的pool
    var pool *redis.Pool
    
    //当启动程序时 就初始化连接池
    
    func init()  {
        pool = &redis.Pool{
            Dial: func() (conn redis.Conn, err error) {
                return redis.Dial("tcp","localhost:6379")  //初始化连接代码
            },
            MaxIdle:         8, //最大空闲连接数
            MaxActive:       0,// 表示和数据库的最大连接数,0 表示没有限制
            IdleTimeout:     100, //最大空闲时间
    
        }
    }
    
    func main()  {
        //先从pool中取出一个连接
        conn := pool.Get()
        defer conn.Close()
    
        _,err :=conn.Do("Set","name","tomcat")
        if err != nil{
            fmt.Println("conn do err=",err)
            return
        }
    
        r,err :=redis.String(conn.Do("Get","name"))
        if err!= nil{
            fmt.Println("conn.do err = ",err)
            return
        }
        fmt.Println("r=",r)
        //如果我们从pool中取出连接,一定要保证连接池是没有关闭的
        
    }
    

    实现功能-显示客户登录菜单

    界面


    界面

    代码实现

    package main
    
    import (
        "fmt"
    
    )
    
    //一个表示用户id 一个表示用户密码
    var usrId int
    var usrpwd string
    
    func main()  {
        //接收用户的选择
        var key int
        //判断是否继续显示菜单
        var loop = true
    
        for  loop{
            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
            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("请用户输入自己的ID")
            fmt.Scanf("%s\n",&usrpwd)
            //先把登录的函数  写到另外一个文件中  login.go
    
            err := login(usrId,usrpwd)
            if err!=nil {
                fmt.Println("登录失败")
            }else {
                fmt.Println("登录成功")
            }
        }else if key == 2{
            fmt.Println("进行用户注册")
        }
    
    }
    

    login.go

    package main
    
    import "fmt"
    
    //写一个函数 完成一个登录
    
    func login(usrId int,usrpwd string)(err error)  {
        //下一步要开始定协议
        fmt.Printf("userId= %d   usrpwd=%s\n",usrId,usrpwd)
        return nil
    }
    
    

    实现用户登录

    结构

    message的组成示意图 和发送一个message的流程

    流程图

    1 完成客户端可以发送的消息的长度,服务端可以正常收到该长度

    确定message格式和结构
    根据上图的分析完成代码

    示意图


    示意图

    2 完成客户端可以正常发送消息本身,服务端可以正常接收消息,并根据客户端发送的(LoginMes)判断用户的合法性,并返回相应的LoginResMes

    思路分析:

    • 让客户端发送消息
    • 服务器端接收到消息,然后反序列化成对应的消息结构体
    • 服务器端根据反序列化成对应的消息,判断是否登录用户是合法的用户,返回LoginResMes
    • 客户解析返回的LoginResMes,显示对应的界面
    • 我们这里需要做函数的封装
    协成处理

    程序结构的改进

    程序结构的改进,前面的程序虽然完成了功能,但是没有结构,系统的可读性,扩展性和维护性,都不好,因此需要对程序结构进行改进

    1 先改进服务器端,先画出程序的框架图,再写代码

    思路

    步骤

    • 先把分析出来的文件创建好,然后放到相应的文件夹中

    代码结构


    代码结构

    详细代码

    package main
    
    import (
        "awesomeProject/chatroom/common/message"
        "encoding/binary"
        "encoding/json"
        "fmt"
        "net"
    )
    
    //写一个函数 完成一个登录
    
    func 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
        }
    
        //这里还需要处理服务器端返回的消息
        mes,err = readPkg(conn)
    
        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("用户登录成功")
        }else if loginResMes.Code == 500{
            fmt.Println(loginResMes.Error)
        }
        return
    }
    

    login.go

    package main
    
    import (
        "fmt"
    
    )
    
    //一个表示用户id 一个表示用户密码
    var usrId int
    var usrpwd string
    
    func main()  {
        //接收用户的选择
        var key int
        //判断是否继续显示菜单
        var loop = true
    
        for  loop{
            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
            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.go

    package main
    
    import (
        "awesomeProject/chatroom/common/message"
        "encoding/binary"
        "encoding/json"
        "fmt"
        "net"
    )
    
    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
    }
    

    client/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 {
    
    }
    

    message.go

    package main
    
    import (
        "fmt"
        "net"
    )
    
    //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
        }
    
    }
    
    func main()  {
    
        //提示信息
        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.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
                }else {
                    fmt.Println("readpkg err =",err)
                    return
                }
            }
            fmt.Println("mes = ",mes)
            err = this.serverProcessMessage(&mes)
            if err != nil{
                return
            }
        }
    }
    
    

    server/main/processor.go

    package process
    

    smsProcessor.go

    package process
    
    import (
        "awesomeProject/chatroom/common/message"
        "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
    
        //如果用户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
    }
    
    

    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
    }
    

    utils/utils.go

    相关文章

      网友评论

          本文标题:海量用户及时通讯系统

          本文链接:https://www.haomeiwen.com/subject/txruphtx.html