一、基于tcp的网络聊天系统
1、client
(1)main包
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("请输入正确的选项") } } }
(2)model包
package model import ( "go_code/project01/communication/common/message" "net"
) type CurUser struct { Conn net.Conn message.User }
(3)process包
(3.1)server类
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) } }
(3.2)smsMgr类
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) }
(3.3)smsProcess类
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 }
(3.4)userMgr类
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) } }
(3.5)userProcess类
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 }
(4)utils包
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 }
2、server
(1)main包
(1.1)main类
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) } }
(1.2)processor类
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 } } }
(1.3)redis类
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)
}, } }
(2)model包
(2.1)error类
package model import "errors" //自定义错误类型 var ( ERROR_USER_NOTEXISTS =
errors.New("用户不存在") ERROR_USER_EXISTS = errors.New("用于已存在") ERROR_USER_PWD =
errors.New("密码错误") )
(2.2)userDao类
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)process包
(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 }
(3.2)userMgr类
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 }
(3.3)userProcess类
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 } }
(4)utils包
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 }