package main
import (
"fmt"
"go_code/project01/communication/client/process"
"os"
)
//用户id
var userId int
//用户密码
var userPwd string
//用户密码
var userName string
func main() {
//用于接收用户输入
key := ""
//显示菜单
for {
fmt.Println("----------多人聊天系统----------")
fmt.Println("\t\t\t\t 1 登陆聊天室")
fmt.Println("\t\t\t\t 2 注册用户")
fmt.Println("\t\t\t\t 3 退出系统")
fmt.Print("请选择功能(1—3):")
fmt.Scanln(&key)
switch key {
case "1":
fmt.Println("登陆聊天室")
//用户登录
fmt.Println("请输入用户的id:")
fmt.Scanf("%d\n",&userId)
fmt.Println("请输入用户的密码:")
fmt.Scanf("%s\n",&userPwd)
//完成登录
userProcess := process.UserProcess{}
userProcess.Login(userId,userPwd)
case "2":
fmt.Println("注册用户")
fmt.Println("请输入用户Id:")
fmt.Scanln(&userId)
fmt.Println("请输入用户密码:")
fmt.Scanln(&userPwd)
fmt.Println("请输入用户名:")
fmt.Scanln(&userName)
//完成注册
userProcess := process.UserProcess{}
userProcess.Register(userId,userPwd,userName)
case "3":
fmt.Println("退出系统")
//loop = false
os.Exit(0)
default:
fmt.Println("请输入正确的选项")
}
}
}
package model
import (
"go_code/project01/communication/common/message"
"net"
)
type CurUser struct {
Conn net.Conn
message.User
}
package process
import (
"encoding/json"
"fmt"
"go_code/project01/communication/client/utils"
"go_code/project01/communication/common/message"
"net"
"os"
)
//定义一个变量用于用户输入
var key string
//发送的内容
var content string
func ShowMenu() {
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 4 退出系统")
fmt.Print("请选择功能(1~4):")
fmt.Scanln(&key)
process := SmsProcess{}
switch key {
case "1":
fmt.Println("\t\t\t\t 1 显示在线用户列表")
outputOnlineUser()
case "2":
fmt.Println("发送消息")
fmt.Scanln(&content)
process.SendGroupMes(content)
case "3":
fmt.Println("消息列表")
case "4":
fmt.Println("退出系统")
os.Exit(0)
default:
fmt.Println("请输入正确的选项")
}
}
//和服务器端保持通信
func serverProcessMes(Conn net.Conn) {
//创建transfer
transfer := utils.Transfer{
Conn:Conn,
}
for {
mes, err := transfer.ReadPkg()
if err != nil {
fmt.Println("服务器出错了",err)
return
}
switch mes.Type {
case message.NotifyUserStatusMesType: //有人上线
var notifyUserStatusMes message.NotifyUserStatusMes
err := json.Unmarshal([]byte(mes.Data), ¬ifyUserStatusMes)
if err != nil {
fmt.Println("反序列化错误:",err)
return
}
updateUserStatus(¬ifyUserStatusMes)
case message.SmsMesType: //群发消息
outputGroupMes(&mes)
default:
fmt.Println("返回消息状态不对")
}
//fmt.Println("mes:",mes)
}
}
package process
import (
"encoding/json"
"fmt"
"go_code/project01/communication/common/message"
)
func outputGroupMes(mes *message.Message) {
//反序列化
var smsMes message.SmsMes
err := json.Unmarshal([]byte(mes.Data), &smsMes)
if err != nil {
fmt.Println("反序列化错误:",err.Error())
return
}
info, _ := fmt.Printf("用户id:\t%d 对大家说:\t%s",smsMes.UserId,smsMes.Content)
fmt.Println(info)
}
package process
import (
"encoding/json"
"fmt"
"go_code/project01/communication/client/utils"
"go_code/project01/communication/common/message"
)
type SmsProcess struct {
}
//发送群聊消息
func (this *SmsProcess) SendGroupMes(content string)(err error) {
//创建Mes
var mes message.Message
mes.Type = message.SmsMesType
//创建一个SmsMes
var smsMes message.SmsMes
smsMes.Content = content //发送的内容
smsMes.UserId = CurUser.UserId
smsMes.UserStatus = CurUser.UserStatus
//将smdMes序列化
data, err := json.Marshal(smsMes)
if err != nil {
fmt.Println("序列化错误:",err)
return
}
mes.Data = string(data)
data, err = json.Marshal(mes)
if err != nil {
fmt.Println("序列化错误:",err)
return
}
//将mes发送到服务器
transfer := utils.Transfer{
Conn: CurUser.Conn,
}
err = transfer.WritePkg(data)
if err != nil {
fmt.Println("发送的消息错误:",err)
return
}
return
}
package process
import (
"fmt"
"go_code/project01/communication/client/model"
"go_code/project01/communication/common/message"
)
//map
var onlineUsers map[int]*message.User = make(map[int]*message.User,10)
var CurUser model.CurUser
//处理返回的数据
func updateUserStatus(notifyUserStatusMes *message.NotifyUserStatusMes) {
//创建User
user , ok := onlineUsers[notifyUserStatusMes.UserId]
if !ok {
user = &message.User{
UserId: notifyUserStatusMes.UserId,
}
}
user.UserStatus = notifyUserStatusMes.Status
onlineUsers[notifyUserStatusMes.UserId] = user
outputOnlineUser()
}
//显示当前在线用户
func outputOnlineUser() {
fmt.Println("当前在线用户列表:")
for id, _ := range onlineUsers {
fmt.Println("用户id",id)
}
}
package process
import (
"encoding/binary"
"encoding/json"
"fmt"
"go_code/project01/communication/client/utils"
"go_code/project01/communication/common/message"
"net"
"os"
)
type UserProcess struct {
}
//完成注册功能
func (this *UserProcess) Register(userId int,userPwd,userName string) (err error) {
//连接服务器
conn, err := net.Dial("tcp", "localhost:8888")
if err != nil {
fmt.Println("连接服务器错误:",err)
return
}
defer conn.Close()
//发送消息给服务器
var mes message.Message
mes.Type = message.RegisterMesType
loginMes := message.RegisterMes{}
loginMes.User.UserId = userId
loginMes.User.UserPwd = userPwd
loginMes.User.UserName = userName
//序列化
data, err := json.Marshal(loginMes)
if err != nil {
fmt.Println("json序列化错误:",err)
return
}
mes.Data = string(data)
data, err = json.Marshal(mes)
if err != nil {
fmt.Println("json序列化错误:",err)
return
}
//创建一个Transfer
transfer := utils.Transfer{
Conn:conn,
}
//将data发送给服务器
err = transfer.WritePkg(data)
if err != nil {
fmt.Println("注册发送消息错误",err)
}
mes, err =transfer.ReadPkg()
if err != nil {
fmt.Println("读取服务器的消息错误:",err)
return
}
//将data反序列化
var RegisterResMes message.RegisterResMes
err = json.Unmarshal([]byte(mes.Data), &RegisterResMes)
if RegisterResMes.Code == 200 {
fmt.Println("注册成功,请重新登陆")
os.Exit(0)
for {
ShowMenu()
}
} else {
fmt.Println(RegisterResMes.Error)
os.Exit(0)
}
return
}
//登录功能
func (this *UserProcess) Login(userId int,userPwd string) (err error) {
//连接服务器
conn, err := net.Dial("tcp", "127.0.0.1:8889")
if err != nil {
fmt.Println("连接服务器错误:",err)
return
}
defer conn.Close()
//发送消息给服务器
var mes message.Message
mes.Type = message.LoginMesType
loginMes := message.LoginMes{
UserId: userId,
UserPwd: userPwd,
}
//序列化
data, err := json.Marshal(loginMes)
if err != nil {
fmt.Println("json序列化错误:",err)
return
}
//序列化
mes.Data = string(data)
data, err = json.Marshal(mes)
if err != nil {
fmt.Println("json序列化错误:",err)
return
}
//将data长度转为切片
var pkgLen uint32
pkgLen = uint32(len(data))
var bytes [4]byte
binary.BigEndian.PutUint32(bytes[0:4],pkgLen)
//发送消息长度
n, err := conn.Write(bytes[0:4])
if n != 4 || err != nil {
fmt.Println("长度发送错误:",err)
return
}
//发送消息自身
_, err = conn.Write(data)
if err != nil {
fmt.Println("长度发送错误:",err)
return
}
//读取服务器发送的消息
tf := utils.Transfer{
Conn:conn,
}
mes, err =tf.ReadPkg()
if err != nil {
fmt.Println("读取服务器的消息错误:",err)
return
}
//将data反序列化
var loginResMes message.LoginResMes
err = json.Unmarshal([]byte(mes.Data), &loginResMes)
if loginResMes.Code == 200 {
//初始化curUser
CurUser.Conn = conn
CurUser.UserId = userId
CurUser.UserStatus = message.UserOnline
//开器协程
fmt.Println("当前在线用户列表如下:")
for _, value := range loginResMes.UsersId {
//当前用户不显示自己
if value == userId {
continue
}
fmt.Println("用户id:\t",value)
//对客户端初始化
user := message.User{
UserId: value,
UserStatus: message.UserOnline,
}
onlineUsers[value] = &user
}
fmt.Print("\n\n")
go serverProcessMes(conn)
for {
ShowMenu()
}
} else {
fmt.Println(loginResMes.Error)
}
return
}
package utils
import (
"encoding/binary"
"encoding/json"
"fmt"
"go_code/project01/communication/common/message"
"net"
)
//将方法关联到结构体中
type Transfer struct {
Conn net.Conn
Buf [8096]byte
}
func (this *Transfer) ReadPkg()(mes message.Message,err error) {
n, err := this.Conn.Read(this.Buf[:4])
if n != 4 || err != nil {
//fmt.Println("服务端读取错误:",err)
return
}
//将buf转换成uint32
var pkgLen uint32
pkgLen = binary.BigEndian.Uint32(this.Buf[0:4])
//读取发送过来的消息
n, err = this.Conn.Read(this.Buf[:pkgLen])
if n != int(pkgLen) || err != nil {
//err = errors.New("read pkg header error")
return
}
err = json.Unmarshal(this.Buf[:pkgLen], &mes)
if err != nil {
fmt.Println("反序列化错误:",err)
return
}
return
}
func (this *Transfer) WritePkg(data []byte)(err error) {
//先发送一个长度给客户端
//将data长度转为切片
var pkgLen uint32
pkgLen = uint32(len(data))
binary.BigEndian.PutUint32(this.Buf[0:4],pkgLen)
//发送消息长度
n, err := this.Conn.Write(this.Buf[0:4])
if n != 4 || err != nil {
fmt.Println("长度发送错误:",err)
return
}
//发送data自身
n, err = this.Conn.Write(data)
if n != int(pkgLen) || err != nil {
fmt.Println("发送错误:",err)
return
}
return
}
package main
import (
"fmt"
"go_code/project01/communication/server/model"
"net"
"time"
)
//处理和客户端的通讯
func process(conn net.Conn) {
//关闭连接
defer conn.Close()
processor := Processor{Conn: conn}
err := processor.processTwo()
if err != nil {
fmt.Println("通讯协程错误:",err)
return
}
}
//对UserDao完成初始化
func initUserDao() {
model.MyUserDao = model.NewUserDao(pool)
}
func main() {
//初始化redis连接池
initPool("127.0.0.1:6379",8,0,100*time.Second)
initUserDao()
fmt.Println("服务器开始监听")
listen, err := net.Listen("tcp", "0.0.0.0:8889")
//关闭连接
defer listen.Close()
if err != nil {
fmt.Println("conn err",err)
return
}
for {
conn, err := listen.Accept()
if err != nil {
fmt.Println("客户端连接错误:",err)
}
//启动协程和客户端保持通讯
go process(conn)
}
}
package main
import (
"fmt"
"go_code/project01/communication/common/message"
process2 "go_code/project01/communication/server/process"
"go_code/project01/communication/server/utils"
"io"
"net"
)
type Processor struct {
Conn net.Conn
}
//根据消息种类,调用不同函数
func (this *Processor) ServerProcessMes(mes *message.Message) (err error) {
switch mes.Type {
case message.LoginMesType:
//登录
userProcess := process2.UserProcess{Conn: this.Conn}
err = userProcess.ServerProcessLogin(mes)
case message.RegisterMesType:
//注册
userProcess := process2.UserProcess{Conn: this.Conn}
err = userProcess.ServerProcessRegister(mes)
case message.SmsMesType:
smsProcess := process2.SmsProcess{}
smsProcess.SendGroupMes(mes)
default:
fmt.Println("没有找到相应类型:")
}
return
}
func (this *Processor) processTwo() (err error) {
//读取客户端消息
for {
//创建结构体实例
transfer := utils.Transfer{
Conn: this.Conn,
}
mes, err := transfer.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.ServerProcessMes(&mes)
if err != nil {
return err
}
}
}
package main
import (
"github.com/gomodule/redigo/redis"
"time"
)
//定义一个全局变量
var pool *redis.Pool
func initPool(address string,MaxIdle,MaxActive int,IdleTimeout time.Duration) {
pool = &redis.Pool{
MaxIdle: MaxIdle,//最大空闲连接数
MaxActive: MaxActive,//数据库最大连接数,0表示最大,无限制
IdleTimeout: IdleTimeout,//最大空闲时间
Dial: func() ( redis.Conn, error) { //初始化连接代码
return redis.Dial("tcp",address)
},
}
}
package model
import "errors"
//自定义错误类型
var (
ERROR_USER_NOTEXISTS = errors.New("用户不存在")
ERROR_USER_EXISTS = errors.New("用于已存在")
ERROR_USER_PWD = errors.New("密码错误")
)
package model
import (
"encoding/json"
"fmt"
"github.com/gomodule/redigo/redis"
"go_code/project01/communication/common/message"
)
//启动服务器就初始化一个userDao
var MyUserDao *UserDao
//定义一个结构体
type UserDao struct {
pool *redis.Pool
}
//创建UserDao实列
func NewUserDao(pool *redis.Pool)(userDao *UserDao) {
dao := UserDao{
pool:pool,
}
return &dao
}
//根据用户id返回用户信息
func (this *UserDao) getUserById(conn redis.Conn, id int) (err error,user *message.User) {
reply, err := redis.String(conn.Do("HGet", "users", id))
if err != nil {
if err == redis.ErrNil{ //没有在redis中找到
err = ERROR_USER_NOTEXISTS
}
return
}
//反序列化
user = &message.User{}
err = json.Unmarshal([]byte(reply), user)
if err != nil {
fmt.Println("反序列化错误:",err)
return
}
return
}
//完成用户登录校验
func (this *UserDao) Login(userId int,userPwd string) (err error,user *message.User) {
//取出连接
conn := this.pool.Get()
//关闭连接
defer conn.Close()
//判断用户是否正确
err, user = this.getUserById(conn, userId)
if err != nil {
return
}
if user.UserPwd != userPwd {
err = ERROR_USER_PWD
return
}
return
}
//完成用户注册功能
func (this *UserDao) Register(user *message.User) (err error) {
//取出连接
conn := this.pool.Get()
//关闭连接
defer conn.Close()
//判断用户是否正确
err, _= this.getUserById(conn, user.UserId)
//从redis中查询到用户
if err == nil {
err = ERROR_USER_EXISTS
return
}
//未查询到用户就注册
buf, err := json.Marshal(user)
if err != nil {
fmt.Println("序列化错误")
return
}
//将数据添加到数据库
_, err = conn.Do("HSet", "users", user.UserId, string(buf))
if err != nil {
fmt.Println("保存用户注册信息错误",err)
return
}
return
}
(3.1)smsProcess类
package process
import (
"encoding/json"
"fmt"
"go_code/project01/communication/common/message"
"go_code/project01/communication/server/utils"
"net"
)
type SmsProcess struct {
}
//转发消息
func (this *SmsProcess) SendGroupMes(mes *message.Message)(err error) {
//将转发消息取出
//取出mes内容
var smsMes message.SmsMes
//反序列化
err = json.Unmarshal([]byte(mes.Data), &smsMes)
if err != nil{
fmt.Println("反序列化错误:",err)
return
}
data, err := json.Marshal(mes)
if err != nil {
fmt.Println("序列化错误:",err)
return
}
for id , v := range userMgr.onlineUsers {
//过滤掉自己
if id == smsMes.UserId {
continue
}
this.SendMesToEachOnlineUser(data,v.Conn)
}
return
}
func (this *SmsProcess) SendMesToEachOnlineUser(data []byte,conn net.Conn )(err error) {
//创建一个Transfer实列
transfer := utils.Transfer{Conn: conn}
err = transfer.WritePkg(data)
if err != nil {
fmt.Println("转发消息失败:",err)
return
}
return
}
package process
import "fmt"
//定义一个变量
var userMgr *UserMgr
//定义一个UserMgr结构体
type UserMgr struct {
onlineUsers map[int]*UserProcess
}
//对UserMgr初始化
func init() {
userMgr = &UserMgr{
onlineUsers:make(map[int]*UserProcess,1024),
}
}
//对onlineUsers添加
func (this *UserMgr) AddOnlineUser(up *UserProcess) {
this.onlineUsers[up.UserId] = up
}
//对onlineUsers删除
func (this *UserMgr) DeleteOnlineUser(userId int) {
delete(this.onlineUsers,userId)
}
//返回在线用户
func (this *UserMgr) GetAllOnlineUser() map[int]*UserProcess {
return this.onlineUsers
}
//根据id返回对应的值
func (this *UserMgr) GetOnlineUserById(userId int) (up *UserProcess,err error) {
up,ok := this.onlineUsers[userId]
if !ok {
err = fmt.Errorf("用户",userId,"不存在")
return
}
return
}
package process
import (
"encoding/json"
"fmt"
"go_code/project01/communication/common/message"
"go_code/project01/communication/server/model"
"go_code/project01/communication/server/utils"
"net"
)
type UserProcess struct {
Conn net.Conn //连接
UserId int
}
//处理登录请求
func (this *UserProcess)ServerProcessLogin(mes *message.Message)(err error) {
var loginMes message.LoginMes
err = json.Unmarshal([]byte(mes.Data), &loginMes)
if err != nil {
fmt.Println("反序列化错误:",err)
return
}
//声明一个resMes
var resMes message.Message
resMes.Type = message.LoginResMesType
//声明一个LoginResMes
var loginResMes message.LoginResMes
//判断用户的id和密码
err, user := 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 = 500
loginResMes.Error = err.Error()
} else {
loginResMes.Code = 505
loginResMes.Error = "服务器内部错误"
}
} else {
loginResMes.Code = 200
this.UserId = loginMes.UserId
userMgr.AddOnlineUser(this)
//通知其它在线用户,上线了
this.NotifyOthersOnlineUser(loginMes.UserId)
for key, _ := range userMgr.onlineUsers {
loginResMes.UsersId = append(loginResMes.UsersId,key)
}
fmt.Println(user)
}
//将loginResMes序列化
data, err := json.Marshal(loginResMes)
if err != nil {
fmt.Println("序列化错误:",err)
return
}
resMes.Data = string(data)
//将resMes序列化
data, err = json.Marshal(resMes)
if err != nil {
fmt.Println("序列化错误:",err)
return
}
transfer := utils.Transfer{Conn: this.Conn}
transfer.WritePkg(data)
return err
}
//处理注册请求
func (this *UserProcess) ServerProcessRegister(mes *message.Message)(err error) {
var registerMes message.RegisterMes
err = json.Unmarshal([]byte(mes.Data), ®isterMes)
if err != nil {
fmt.Println("反序列化错误:",err)
return
}
//声明一个resMes
var resMes message.Message
resMes.Type = message.RegisterResMesType
//registerMes
var registerResMes message.RegisterResMes
err = model.MyUserDao.Register(®isterMes.User)
if err != nil {
if err == model.ERROR_USER_EXISTS {
registerResMes.Code = 505
registerResMes.Error = model.ERROR_USER_EXISTS.Error()
} else {
registerResMes.Code = 506
registerResMes.Error = "服务器错误"
}
}else {
registerResMes.Code = 200
}
//将registerResMes序列化
data, err := json.Marshal(registerResMes)
if err != nil {
fmt.Println("序列化错误:",err)
return
}
resMes.Data = string(data)
//将resMes序列化
data, err = json.Marshal(resMes)
if err != nil {
fmt.Println("序列化错误:",err)
return
}
transfer := utils.Transfer{Conn: this.Conn}
transfer.WritePkg(data)
return err
}
//通知在线用户的方法
func (this *UserProcess) NotifyOthersOnlineUser(userId int) {
//遍历发送
for id, up := range userMgr.onlineUsers {
if id == userId {
continue
}
up.NotifyMeOnline(userId)
}
}
func (this *UserProcess) NotifyMeOnline(userId int) {
//组装数据
var mes message.Message
mes.Type = message.NotifyUserStatusMesType
var notifyUserStatusMes message.NotifyUserStatusMes
notifyUserStatusMes.UserId = userId
notifyUserStatusMes.Status = message.UserOnline
//序列化
data, err := json.Marshal(notifyUserStatusMes)
if err != nil {
fmt.Println("序列化错误:",err)
return
}
mes.Data = string(data)
//序列化
data, err = json.Marshal(mes)
if err != nil {
fmt.Println("序列化错误:",err)
return
}
//发送
transfer := utils.Transfer{
Conn:this.Conn,
}
err = transfer.WritePkg(data)
if err != nil {
fmt.Println("发送错误:",err)
return
}
}
package utils
import (
"encoding/binary"
"encoding/json"
"fmt"
"go_code/project01/communication/common/message"
"net"
)
//将方法关联到结构体中
type Transfer struct {
Conn net.Conn
Buf [8096]byte
}
func (this *Transfer) ReadPkg()(mes message.Message,err error) {
n, err := this.Conn.Read(this.Buf[:4])
if n != 4 || err != nil {
//fmt.Println("服务端读取错误:",err)
return
}
//将buf转换成uint32
var pkgLen uint32
pkgLen = binary.BigEndian.Uint32(this.Buf[0:4])
//读取发送过来的消息
n, err = this.Conn.Read(this.Buf[:pkgLen])
if n != int(pkgLen) || err != nil {
//err = errors.New("read pkg header error")
return
}
err = json.Unmarshal(this.Buf[:pkgLen], &mes)
if err != nil {
fmt.Println("反序列化错误:",err)
return
}
return
}
func (this *Transfer) WritePkg(data []byte)(err error) {
//先发送一个长度给客户端
//将data长度转为切片
var pkgLen uint32
pkgLen = uint32(len(data))
binary.BigEndian.PutUint32(this.Buf[0:4],pkgLen)
//发送消息长度
n, err := this.Conn.Write(this.Buf[0:4])
if n != 4 || err != nil {
fmt.Println("长度发送错误:",err)
return
}
//发送data自身
n, err = this.Conn.Write(data)
if n != int(pkgLen) || err != nil {
fmt.Println("发送错误:",err)
return
}
return
}