go開發聊天室(基於tcp協議)

此項目參照韓順平老師教程,特此感謝。

項目結構如下:

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, _ := redis.String(this.Con.Do("hget", "users", mes.UserId))

    fmt.Println("user_old=", user_old)

    if user_old != "" {

        fmt.Println("用戶已存在!!!")

        return fmt.Errorf("用戶已存在!!!")

    }

    user := User{UserId : mes.UserId, UserName : mes.UserName, UserPwd : mes.UserPwd}

    userJson, err := json.Marshal(user)

    if err != nil {

        fmt.Println("userJson, err=", err)

        return err

    }

    _, err = this.Con.Do("hset", "users", mes.UserId, string(userJson))

    if err != nil {

        fmt.Println("hset user err=", err)

        return err

    }

    return nil

}

//保存消息

func (this *UserDao) SaveMes(uid int, toUid int, content string) error {

    str := fmt.Sprintf("%dto%d", uid, toUid)

    _, err := this.Con.Do("hset", str, time.Now().UnixNano(), content)

    if err != nil {

        fmt.Println("SaveMes hset err=", err)

        return err

    }

    return nil

//用戶查看消息記錄,由於redis包識別不了hgetall指令,姑只取一條作示例

func (this *UserDao) SearchMesList(idToId string) string {

    data, err := redis.String(this.Con.Do("hget", idToId, "1576641743776366500"))

    if err != nil {

        fmt.Println("hgetall idToId err=", err)

        return ""

    }

    return data

}

process/userManage.go:

package process

//已上線用戶管理

import (

    "net"

)

type AllLoggedUser struct {

    LoggedUser []map[int]net.Conn

}

 

var (

    allLoggedUser AllLoggedUser

)

func GetLoggedUser() *[]map[int]net.Conn {

    return &allLoggedUser.LoggedUser

}

//已上線變量添加用戶

func (this *AllLoggedUser) AddUser(id int, conn net.Conn) {

    mp := make(map[int]net.Conn)

    mp[id] = conn

    this.LoggedUser = append(this.LoggedUser, mp)

}

//已上線變量刪除下線用戶

func (this *AllLoggedUser) DelUser(id int) {

    for i, val := range this.LoggedUser {

        for uid, _ := range val {

            if uid == id {

                this.LoggedUser = append(this.LoggedUser[:i], this.LoggedUser[i + 1 :]...)

            }

        }

    }

    

}

process/userProcess.go:

package process

 

import (

    "learn/chatroom/common/message"

    "learn/chatroom/common/utils"

    "learn/chatroom/server/model"

    "encoding/json"

    "net"

    "fmt"

)

 

type UserProcess struct {

    Conn net.Conn

}

 

func GetUserProcess(conn net.Conn) *UserProcess {

    return &UserProcess{conn}

}

//登錄

func (this *UserProcess) DoLogin(mes message.Message, mes_back message.Message, resmes message.ResMes)  {

    var data_login message.LoginMes

    var user model.User

    mes_back.Type = -1

    json.Unmarshal([]byte(mes.Data), &data_login)

    err := model.NewUserDao().GetUserById(data_login.UserId, &user)

    if err !=nil {

        resmes.Code = 300

        resmes.Msg = "未註冊!!!"

    } else if data_login.UserPwd == user.UserPwd {

        resmes.Code = 200

        resmes.Msg = "服務端驗證成功!!!"

        //通知其他線上用戶

        for _, mp := range allLoggedUser.LoggedUser {

            for id, conn := range mp {

                if id == user.UserId {

                    continue //跳過自己

                }

                this.NotifyOtherUsers(data_login.UserId, conn, 1)

            }

        }

        //新增已登錄用戶到變量LoggedUser

        allLoggedUser.AddUser(user.UserId, this.Conn)

        


 

    } else {

        resmes.Code = 300

        resmes.Msg = "服務端驗證失敗!!!"

    }

    mesres_json, err := json.Marshal(resmes)

    mes_back.Data = string(mesres_json)

    err = utils.SendMsg(this.Conn, mes_back)

    if err != nil {

        fmt.Println("服務器反饋信息失敗")

    }

}

//註冊

func (this *UserProcess) DoRegister(mes message.Message, mes_back message.Message, resmes message.ResMes) {

    var data_login message.LoginMes

    mes_back.Type = -2

    json.Unmarshal([]byte(mes.Data), &data_login)

    err := model.NewUserDao().AddUser(data_login)

    if err !=nil {

        resmes.Code = 300

        resmes.Msg = "服務端新增用戶失敗!!!"

    } else {

        resmes.Code = 200

        resmes.Msg = "服務端新增用戶成功!!!"

    }

    

    mesres_json, err := json.Marshal(resmes)

    mes_back.Data = string(mesres_json)

    err = utils.SendMsg(this.Conn, mes_back)

    if err != nil {

        fmt.Println("服務器反饋新增信息失敗")

    }

}

//顯示在線用戶

func (this *UserProcess) ShowUserOnline(mes_back message.Message) {

    mes_back.Type = -3

    users := make([]model.User, 10)

    for _, mp := range allLoggedUser.LoggedUser {

        for id, _ := range mp {

            var user model.User

            model.NewUserDao().GetUserById(id, &user)

            users[id] = user

        }

    }

    loggedUserJson, err := json.Marshal(users)

    mes_back.Data = string(loggedUserJson)

    err = utils.SendMsg(this.Conn, mes_back)

    if err != nil {

        fmt.Println("服務器反饋在線用戶信息失敗")

    }

}

//用戶向其他用戶發送消息

func (this *UserProcess) SendMes(mes message.Message, mes_back message.Message) {

    var sendmes message.SendMes

    mes_back.Type = -4

    err := json.Unmarshal([]byte(mes.Data), &sendmes)

    if err != nil {

        fmt.Println("服務端反序列化sendmes失敗!!!")

        return

    }

    last_content := fmt.Sprintf("%d said to %d:%s\n",sendmes.Uid, sendmes.ToUid, sendmes.Content)

    mes_back.Data = last_content

    //保存消息到redis

    err = model.NewUserDao().SaveMes(sendmes.Uid, sendmes.ToUid, last_content)

    if err != nil {

        return

    }

    //用以判斷目標客戶是否在線

    isOnline := false 

    //發送消息到對應的用戶

    for _, mp := range allLoggedUser.LoggedUser {

        for id, conn := range mp {

            if id == sendmes.ToUid {

                isOnline = true

                err = utils.SendMsg(conn, mes_back)

            }

        }

    }

    if isOnline == false {//離線時,起一個攜程監聽客戶上線再發送

        go this.WatchUserIsOnline(sendmes.ToUid, mes_back)

    }

}

//離線發送消息

func (this *UserProcess) WatchUserIsOnline(toUid int, mes_back message.Message) {

    boo := true

    for boo{

        for _, mp := range allLoggedUser.LoggedUser {

            for id, conn := range mp {

                if id == toUid {

                    err := utils.SendMsg(conn, mes_back)

                    if err != nil {

                        fmt.Println("服務端離線發送消息失敗!!!")

                    }

                    boo = false

                }

            }

        }

    }

}

//通知其他用戶上線 onlineOrUnline=1->上線,否則下線

func (this *UserProcess) NotifyOtherUsers(currentId int, conn net.Conn, onlineOrUnline int) {

    var mes message.Message

    var str string

    mes.Type = -5

    if onlineOrUnline == 1 {

        str = fmt.Sprintf("id爲%d的用戶已上線", currentId)

    } else {

        str = fmt.Sprintf("id爲%d的用戶已下線", currentId)

    }

    mes.Data = str

    err := utils.SendMsg(conn, mes)

    if err != nil {

        fmt.Println("服務端推送消息失敗")

    }

}

 

//用戶查看消息記錄

func (this *UserProcess) SearchMesList(mes message.Message, mes_back message.Message) {

    content :=  model.NewUserDao().SearchMesList(mes.Data)

    mes_back.Type = -6

    mes_back.Data = content

    err := utils.SendMsg(this.Conn, mes_back)

    if err != nil {

        fmt.Println("服務端查詢消息列表失敗")

    }

}

//發送文件

func (this *UserProcess) SendFile(mes message.Message, mes_back message.Message) {

    var fileMes message.FileMes

    json.Unmarshal([]byte(mes.Data), &fileMes)

    mes_back.Type = -7

    mes_back.Data = mes.Data

    for _, mp := range allLoggedUser.LoggedUser {

        for id, conn := range mp {

            if id == fileMes.ToUid {

                err := utils.SendMsg(conn, mes_back)

                if err != nil {

                    fmt.Println("服務端發送文件失敗!!!")

                }

            }

        }

    }

}

---------------------------------------------------------------------------分界線------------------------------------------------------------------------------------
client:

main/main.go:

package main

 

import (

    "fmt"

    "learn/chatroom/client/process"

    "net"

    "os"

)

 

func main() {

    var key int

    var loop = true

    var uid int

    var name string

    var pwd string

    conn := GetConn()

    for {

        mainMenu(&loop, &key)

        switch key {

            case 1 :

                toLogin(&uid, &pwd, conn)

                //登錄後顯示菜單

                process.ShowMenu(conn, uid)

            case 2 :

                toRegister(&uid, &name, &pwd, conn)

            case 3 :

                os.Exit(0)

        }

        

    }

    

}

 

func GetConn() net.Conn {

    //建立連接

    conn, err_conn := net.Dial("tcp", "192.168.2.149:9999")

    if err_conn != nil {

        fmt.Println("conn err =", err_conn)

    }

    return conn

}

func toLogin(uid *int, pwd *string, conn net.Conn){

    for {

        fmt.Println("請輸入用戶id")

        fmt.Scanln(uid)

        fmt.Println("請輸入用戶密碼")

        fmt.Scanln(pwd)

        userProcess := process.GetUserProcess(conn)

        err_login := userProcess.DoLogin(uid, pwd)

        if err_login != nil {

            fmt.Println("用戶名或密碼錯誤,請重新輸入")

        } else {

            break

        }

    }

}

 

func toRegister(uid *int, name *string, pwd *string, conn net.Conn) {

    for {

        fmt.Println("請輸入用戶id")

        fmt.Scanln(uid)

        fmt.Println("請輸入用戶暱稱")

        fmt.Scanln(name)

        fmt.Println("請輸入用戶密碼")

        fmt.Scanln(pwd)

        userProcess := process.GetUserProcess(conn)

        err := userProcess.DoRegister(uid, name, pwd)

        if err != nil {

            fmt.Println("註冊失敗,請重新輸入")

        } else {

            break

        }

    }

    

}

func mainMenu(loop *bool, key *int) {

    for *loop {

        fmt.Println("------------------------歡迎使用多人聊天系統----------------------------")

        fmt.Println("\t\t\t 1 登錄聊天室 ")

        fmt.Println("\t\t\t 2 註冊用戶 ")

        fmt.Println("\t\t\t 3 退出系統 ")

        fmt.Println("\t\t\t  請選擇 1-3 ")

 

        fmt.Scanln(key)

        switch *key {

            case 1 : 

                fmt.Println(" 登錄聊天室 ")

                *loop = false

            case 2 :

                fmt.Println(" 註冊用戶 ")

                *loop = false

            case 3 :

                fmt.Println(" 退出系統 ")

                *loop = false

            default :

                fmt.Println(" 輸入錯誤,請重新輸入 ")

        }

    }

}

model/user.go:

package model

 

type User struct {

    UserId int `json:"id"`

    UserName string `json:"name"`

    UserPwd string `json:"pwd"`

}

process/server.go:

package process

 

import (

    "fmt"

    "os"

    "net"

    "learn/chatroom/common/utils"

    "learn/chatroom/common/message"

    "learn/chatroom/client/model"

    "encoding/json"

    "io/ioutil"

)

//登錄後菜單

func ShowMenu(conn net.Conn, uid int) {

    fmt.Println("-------------------恭喜xxx登陸成功----------------------")

    var num int 

    up := GetUserProcess(conn)

    for {

            fmt.Println(" 1.顯示在線用戶 ")

            fmt.Println(" 2.發送消息")

            fmt.Println(" 3.信息列表")

            fmt.Println(" 4.發送文件")

            fmt.Println(" 5.退出系統")

            fmt.Println("請選擇1-4")

            fmt.Scanln(&num)

            switch num {

                case 1 :

                    up.ShowUserOnline()

                case 2 :

                    up.SendMes(uid)

                case 3 :

                    up.SearchMesList(uid)

                case 4 :

                    up.SendFile(uid)

                case 5 :

                    fmt.Println("退出系統")

                    os.Exit(0)

                default :

                    fmt.Println("輸入錯誤,請重新輸入")

            }

            

    }

}

//監聽服務端推送消息

func ServerProcessMes(conn net.Conn) {

    //fmt.Println("ServerProcessMes 等待服務器消息")

    for {

        var mes message.Message

        err := utils.ReceiveMsg(conn, &mes)

        if err != nil {

            fmt.Println("ServerProcessMes utils.ReceiveMsg fail")

            return

        }

        //fmt.Println("ServerProcessMes 獲取的mes=", mes)

        switch mes.Type {

            case -3 : 

                users := make([]model.User, 10)

                json.Unmarshal([]byte(mes.Data), &users)

                fmt.Println("在線用戶如下:")

                for _, user := range users {

                    if user.UserId > 0 {

                        fmt.Printf("用戶id=%d\tname=%s\tpwd=%s\n", user.UserId, user.UserName, user.UserPwd)

                    }

                    

                }

            case -4, -5, -6: 

                fmt.Println(mes.Data)

            case -7 :

                ReceiveFile(mes)

            default :

                fmt.Println("ServerProcessMes mes", mes)

        }

    }

}

//接受文件

func ReceiveFile(mes message.Message) {

    var fileMes message.FileMes

    json.Unmarshal([]byte(mes.Data), &fileMes)

    var path string

    path = fmt.Sprintf("d:/ctfiles/%s", fileMes.FileName)

    err := ioutil.WriteFile(path, fileMes.Content, 0666)

    if err != nil {

        fmt.Println("write err=", err)

    } else {

        str := fmt.Sprintf("接收用戶id=%d的文件%s成功!!!", fileMes.Uid, fileMes.FileName)

        fmt.Println(str)

    }

}

process/userProcess.go:

package process

//客戶端用戶業務

import (

    "fmt"

    "learn/chatroom/common/message"

    "learn/chatroom/common/utils"

    "net"

    "encoding/json"

    "io/ioutil"

    "strings"

)

 

type UserProcess struct {

    Conn net.Conn

}

//實例化

func GetUserProcess(conn net.Conn) *UserProcess {

    return &UserProcess{Conn : conn}

}

//登錄請求

func (this *UserProcess) DoLogin(uid *int, pwd *string ) error {

    //mes爲發送到服務端信息,mes_back爲服務端反饋信息

    var mes, mes_back message.Message

    logmes := message.LoginMes{UserId : *uid, UserPwd : *pwd}

    mes.Type = 1

    loginJson, err := json.Marshal(logmes)

    if err != nil {

        fmt.Println("logindata marshal err=", err)

        return err

    }

    mes.Data = string(loginJson)

    err_send := utils.SendMsg(this.Conn, mes)

    if err_send != nil {

        return err_send

    }

    err_receive := utils.ReceiveMsg(this.Conn, &mes_back)

    if err_receive != nil {

        fmt.Println("客戶端接收反饋信息失敗")

        return err_receive

    }

    //resmes爲服務端反饋的具體信息

    var resmes message.ResMes

    json.Unmarshal([]byte(mes_back.Data), &resmes)

    //反饋登陸成功

    if mes_back.Type == -1 && resmes.Code == 200 {

        fmt.Println(resmes)

        //監聽服務端的消息,如好友上線,發來消息等

        go ServerProcessMes(this.Conn)

        return nil

    }

    return fmt.Errorf("客戶端接收反饋信息錯誤")

}

//註冊請求

func (this *UserProcess) DoRegister(uid *int, name *string, pwd *string) (err error) {

    //mes爲發送到服務端信息,mes_back爲服務端反饋信息

    var mes, mes_back message.Message

    logmes := message.LoginMes{UserId : *uid, UserName : *name, UserPwd : *pwd}

    mes.Type = 2

    registerJson, err := json.Marshal(logmes)

    if err != nil {

        fmt.Println("register-data marshal err=", err)

        return 

    }

    mes.Data = string(registerJson)

    err = utils.SendMsg(this.Conn, mes)

    if err != nil {

        return 

    }

    err = utils.ReceiveMsg(this.Conn, &mes_back)

    if err != nil {

        fmt.Println("客戶端接收反饋信息失敗")

        return 

    }

    //resmes爲服務端反饋的具體信息

    var resmes message.ResMes

    json.Unmarshal([]byte(mes_back.Data), &resmes)

    //反饋註冊成功

    if mes_back.Type == -2 && resmes.Code == 200 {

        fmt.Println(resmes.Msg)

        err = nil

        return 

    }

    err = fmt.Errorf("客戶端接收反饋信息錯誤")

    return

}

//顯示在線用戶

func (this *UserProcess) ShowUserOnline() {

    var mes message.Message

    mes.Type = 3

    err := utils.SendMsg(this.Conn, mes)

    if err != nil {

        fmt.Println("客戶端發送反饋信息失敗")

        return 

    }

}

//用戶發送消息

func (this *UserProcess) SendMes(uid int) {

    var toUid int

    var content string

    fmt.Println("請輸入想聊天對象的id:")

    fmt.Scanln(&toUid)

    fmt.Println("請輸入聊天內容:")

    fmt.Scanln(&content)

    fmt.Printf("%d said to %d: %s\n", uid, toUid, content)

    var mes message.Message

    mes.Type = 4

    var dataSend message.SendMes

    dataSend.Uid = uid

    dataSend.ToUid = toUid

    dataSend.Content = content

    dataJson, err := json.Marshal(dataSend)

    mes.Data= string(dataJson)

    if err != nil {

        fmt.Println("json.Marshal(data)失敗")

        return 

    }

    err = utils.SendMsg(this.Conn, mes)

    if err != nil {

        fmt.Println("客戶端發送信息失敗")

        return 

    }

}

//查看消息列表

func (this *UserProcess) SearchMesList(uid int) {

    var toid int

    var mes message.Message

    fmt.Println("請輸入目標用戶的id")

    fmt.Scanln(&toid)

    content := fmt.Sprintf("%dto%d", uid, toid)

    mes.Type = 6

    mes.Data = content

    err := utils.SendMsg(this.Conn, mes)

    if err != nil {

        fmt.Println("查看消息列表失敗")

    }

}

//發送文件

func (this *UserProcess) SendFile(uid int) {

    var filePath string

    var toUid int

    var fileName string

    var mes message.Message

    var fileMes message.FileMes

    fmt.Println("請輸入文件地址:")

    fmt.Scanln(&filePath)

    fmt.Println("請輸入接收對象id:")

    fmt.Scanln(&toUid)

    //判斷文件是否存在

    ok, _ := utils.PathExists(filePath)

    if !ok {

        fmt.Println("文件夾不存在!!!")

        return

    }

    //獲取文件名和內容

    strs := strings.Split(filePath, "/")

    fileName = strs[len(strs) - 1]

    content, err_cont := ioutil.ReadFile(filePath)

    if err_cont != nil {

        fmt.Println("讀取文件內容失敗!!!")

        return

    }

    //封裝消息體

    fileMes.Uid = uid

    fileMes.ToUid = toUid

    fileMes.FileName = fileName

    fileMes.Content = content

    fileJson, _ := json.Marshal(fileMes)

 

    mes.Type = 7

    mes.Data = string(fileJson)

    err := utils.SendMsg(this.Conn, mes)

    if err != nil {

        fmt.Println("發送文件失敗!!!")

    }

    fmt.Println("發送文件成功!!!")

}

--------------------------------------------------------------------------------------分界線-------------------------------------------------------------------------

common:

message/message.go:

package message

 

type Message struct {

    //類型 1-登錄-1-登陸反饋 2-註冊 -2 -註冊反饋 3-顯示在線用戶 -3,-顯示在線用戶反饋 4-用戶向用戶發送信息 -4,反饋 -5,-服務器推送消息

    //6-用戶查看消息列表 -6,反饋 7-用戶發送文件 -7,反饋

    Type int 

    Data string //數據 

}

 

type LoginMes struct {

    UserId int `json:"id"`

    UserPwd string `json:"pwd"`

    UserName string `json:"name"`

}

 

type ResMes struct {

    Code int //返回狀態碼 100-未註冊 200-成功  300-失敗

    Msg string //信息

}

 

type SendMes struct {

    Uid int `json:"uid"`

    ToUid int `json:"touid"`

    Content string `json:"content"`

}

type FileMes struct {

    Uid int

    ToUid int

    FileName string

    Content []byte

}

utils/cli_to_ser.go:

package utils

//客戶端和服務端相互推送消息

import (

    "net"

    "learn/chatroom/common/message"

    "encoding/json"

    "encoding/binary"

    "fmt"

    "os"

)

 

func SendMsg(conn net.Conn, mes message.Message) error {

    mesJson, err_mesJson := json.Marshal(mes)

    if err_mesJson != nil {

        fmt.Println("mes marshal err=", err_mesJson)

        return err_mesJson

    }

    //定義傳輸字節長度

    meslen := uint32(len(mesJson))

    buf := make([]byte, 4)

    //uint32轉成[]byte,以便conn.Write()

    binary.BigEndian.PutUint32(buf[:4], meslen)  

    //發送數據長度

    n, err_write := conn.Write(buf[:4])

    if err_write !=nil || n != 4 {

        fmt.Println("mes-length write err=", err_write)

        return err_write

    }

    //fmt.Printf("發送了%v個字節\n", len(mesJson))

    //fmt.Println("發送的data=", string(mesJson))

    //發送登錄數據

     _, err_write = conn.Write(mesJson)

    if err_write !=nil {

        fmt.Println("mes-data write err=", err_write)

        return err_write

    } 

    return nil

}

 

func ReceiveMsg(conn net.Conn, mes *message.Message) (error){

    //定義接收字節變量

    buf := make([]byte, 8192)

    //conn.Read()等待客戶端發送消息,如果沒有write[發送],那麼協程阻塞在這裏

    //讀取前四個字節,數據長度

    n, err := conn.Read(buf[:4])

    if err != nil || n != 4 {

        fmt.Println("server read len err=", err)

        fmt.Println("客戶端退出。。。")

        return err

    }

    //獲取接受的數據長度。將[]byte轉成uint32

    data_len := binary.BigEndian.Uint32(buf[:4])

    //接收已知長度的數據

    n, err = conn.Read(buf[:data_len])

    if n != int(data_len) || err !=nil {

        fmt.Println("server read data err=", err)

        fmt.Println("客戶端退出。。。")

        return err

    }

    //fmt.Println("接收到的data_len=", data_len)

    //fmt.Println("接收到的data=", string(buf[:data_len]))

    json.Unmarshal(buf[:data_len], mes)

    return nil

}

 

//判斷文件文件是否存在

func PathExists(path string) (bool, error) {

    _, err := os.Stat(path)

    if err == nil {

        return true, nil

    }

    if os.IsNotExist(err) {

        return false, nil

    }

    return false, err

}

以上就是全部

 

 

 

 

發佈了29 篇原創文章 · 獲贊 6 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章