此项目参照韩顺平老师教程,特此感谢。
项目结构如下:
client,server分别是客户端和服务端代码包,common为公共文件函数包;
main,model,processf分别存放主程序文件,结构体文件和方法文件,其中server下model中的Dao.go是redis连接池配置文件,引用了redis的git代码包地址:https://github.com/gomodule/redigo,下载后按说明引用。
运行项目:
1.开启服务端
2.开启客户端(一个或多个)
3.注册并登录后显示
4.再登陆几个客户端,就可以互相发送消息和传送文件以及其他用户上下线服务端消息提醒了
代码如下:
server端:
main/main.go:
package main
import (
"learn/chatroom/server/mainProcess"
"learn/chatroom/server/model"
"time"
)
func main() {
//初始化连接池得到全局连接池变量pool
model.PoolInit(8, 0, time.Second * 100, "tcp", "localhost:6379")
//启动服务端进程
mainProcess.GetMainProcess().OnlyProcess()
}
mainProcess/mainProcess.go:
package mainProcess
import (
"fmt"
"net"
"learn/chatroom/common/utils"
"learn/chatroom/common/message"
"learn/chatroom/server/process"
)
type MainProcess struct {
Conn net.Conn
}
func GetMainProcess() *MainProcess {
return &MainProcess{}
}
func (this *MainProcess) OnlyProcess() {
fmt.Println("server开始监听。。。")
listen, err := net.Listen("tcp", "0.0.0.0:9999")
if err != nil {
fmt.Println("listen err=", err)
return
}
defer listen.Close()
for {
fmt.Println("等待客户端连接。。。")
//等待并返回下一个连接到该接口的连接
conn, err_accp := listen.Accept()
this.Conn = conn
if err_accp != nil {
fmt.Println("err_accp=", err_accp)
} else {
fmt.Printf("客户端network=%v,ip=%v连接成功\n", conn.RemoteAddr().Network(), conn.RemoteAddr().String())
//协程处理多个连接
go this.process(conn)
}
}
}
//登录后处理其他业务
func (this *MainProcess) process(conn net.Conn) {
user := process.GetUserProcess(conn)
defer conn.Close()
defer func (){//匿名函数处理用户下线业务
var uid int
loggedUser := process.GetLoggedUser()
//获取当前用户id
for i, mp := range *loggedUser {
for id, co := range mp {
if co == conn {
uid = id
//去掉当前下线用户
*loggedUser = append((*loggedUser)[:i], (*loggedUser)[i + 1:]...)
}
}
}
//fmt.Println("unline uid=", uid)
//说明未获取当前用户id
if uid == 0 {
return
}
//通知其他线上用户
for _, mp := range *loggedUser {
for _, co := range mp {
if co == conn {
continue
}
user.NotifyOtherUsers(uid, co, 0)
}
}
}()
var mes, mes_back message.Message
var resmes message.ResMes
for {
err := utils.ReceiveMsg(conn, &mes)
if err != nil {
fmt.Println("服务器接受信息失败")
return
}
switch mes.Type {
case 1 ://登录类型请求
user.DoLogin(mes, mes_back, resmes)
case 2 ://注册类型请求
user.DoRegister(mes, mes_back, resmes)
case 3 ://显示在线用户
user.ShowUserOnline(mes_back)
case 4 ://用户发送消息
user.SendMes(mes, mes_back)
case 6 ://查看消息
user.SearchMesList(mes, mes_back)
case 7 ://发送文件
user.SendFile(mes, mes_back)
}
}
}
model/Dao.go:
package model
import (
"github.com/garyburd/redigo/redis"
"time"
)
var pool *redis.Pool
func PoolInit(maxIdle int, maxActive int, idleTimeout time.Duration, network string, address string) {
//初始化连接池
pool = &redis.Pool{
MaxIdle : maxIdle,
MaxActive : maxActive,
IdleTimeout : idleTimeout,
Dial : func() (redis.Conn, error){
return redis.Dial(network, address)
},
}
}
model/user.go:
package model
type User struct {
UserId int `json:"id"`
UserName string `json:"name"`
UserPwd string `json:"pwd"`
}
model/userDao.go:
package model
import (
"github.com/garyburd/redigo/redis"
"fmt"
"encoding/json"
"learn/chatroom/common/message"
"time"
)
type UserDao struct {
Con redis.Conn
}
func NewUserDao() *UserDao {
return &UserDao{pool.Get()}
}
//根据id获取user
func (this *UserDao) GetUserById(id int, user *User) error {
user2, err := redis.String(this.Con.Do("hget", "users", id))
if err != nil {
fmt.Println("redis.String err=", err)
return err
}
err = json.Unmarshal([]byte(user2), user)
if err != nil {
fmt.Println("json.Unmarshal err=", err)
return err
}
return nil
}
//新增user
func (this *UserDao) AddUser(mes message.LoginMes) error {
//先检查用户id是否重复
user_old, _ := re