1. 程式人生 > 實用技巧 >GoLang Socket 聊天例項

GoLang Socket 聊天例項

收發訊息實體

package model

// 傳送或者接收的訊息資訊
type SmsMessage struct {
    // 訊息 型別  1 註冊   2登陸  3線上狀態  4私聊資訊   5群發信息 6下線線狀態   7 服務端返回資訊  8 服務端返回客戶端傳送失敗或錯誤資訊
    Type int32 `json:"type"`
    // 訊息體
    Data string `json:"data"`
}

// 登陸請求資訊
type SmsLogin struct {
    UserId int32 `json:"userid"`
    Pwd string   `json:"
pwd"` UserName string `json:username` } // 傳送註冊資訊 type SmsRegister struct { UserInfo } // 傳送線上狀態資訊 type SmsLineStatus struct { // 當前使用者Id UserId int32 `json:"userid"` // 線上狀態 1, 線上,2為 離線 Status int32 `json:"status"` } // 私聊資訊 type SmsToOne struct { // 資訊傳送者Id SendUserId int32 `json:"
senduserid"` // 接收者Id ToUserId int32 `json:"touserid"` // 傳送內容 Context string `json:"context"` } // 群發信息 type SmsToAll struct { // 訊息傳送者Id SendUserId int32 `json:"senduserid"` Context string `json:"context"` } // 服務端返回客戶端資訊 type SmsResponse struct { Code int32 `json:"code"` OnLineUser []int32 `json:
"onlineuser"` Error string `json:"error"` } // 服務端返回客戶端傳送失敗或錯誤資訊 type SmsErrorResponse struct { Code int32 `json:"code"` Error string `json:"error"` }

使用者資訊實體

package model

import "net"

// 使用者資訊
type UserInfo struct {
    UserId int32 `json:"userid"`
    UserName string `json:"username"`
    Pwd string `json:"pwd"`
}

type UserLineMsg struct {
    UserId int32 `json:"userid"`
    Conn net.Conn `json:"conn"`
}

客戶端收發訊息 服務Struct

package services

import (
    "MySocket/Commit/model"
    "encoding/binary"
    "encoding/json"
    "fmt"
    "net"
)
// 客戶端讀寫實體
type SmsReadWriteClientService struct{
    Conn net.Conn
}

// 客戶端讀取服務端發來的資料
func (this *SmsReadWriteClientService) ReadPkg() (sms model.SmsMessage,err error){
    fmt.Println("客戶端讀取伺服器端發來的資料……")
    var Buf [8096]byte
    _,err =this.Conn.Read(Buf[:4])
    if err!=nil{
         fmt.Println("客戶端讀取伺服器端發來的資料長度出錯:",err)
         return
    }
    var pkgLen uint32
    pkgLen=binary.BigEndian.Uint32(Buf[0:4])
    n,err:=this.Conn.Read(Buf[:pkgLen])
    if n!=int(pkgLen){
        fmt.Println("客戶端讀取伺服器端發來的資料長度與接收的長度不致!")
        return
    }
    if err!=nil{
        fmt.Println("客戶端讀取伺服器端發來的資料出錯:",err)
        return
    }
    err =json.Unmarshal(Buf[:pkgLen],&sms)
    if err!=nil{
        fmt.Println("客戶端讀取伺服器端發來的資料返序列化出錯:",err)
        return
    }
    return
}

// 客戶端發關訊息到伺服器
func (this *SmsReadWriteClientService) WritePkg(data []byte) (err error){
    var Buf [8096]byte
   var pkgLen uint32
   pkgLen=uint32(len(data))
   binary.BigEndian.PutUint32(Buf[0:4],pkgLen)
   // 傳送長度
   n,err :=this.Conn.Write(Buf[:4])
   if n!=4 {
       fmt.Println("客戶端傳送資訊長度寫實際長度不一致!")
          return
   }
   if err!=nil{
       fmt.Println("客戶端傳送資訊長度資訊錯誤:",err)
       return
   }

   // 傳送訊息本身
   n,err =this.Conn.Write(data)
   if n!=int(pkgLen) {
       fmt.Println("客戶端傳送訊息體寫實際長度不一致!")
   }
   if err!=nil{
       fmt.Println("客戶端傳送訊息體寫錯誤:",err)
   }
   return
}

客戶端思路:

定義全域性變數

package services
import (
    "MySocket/Client/model"
)
type  ConstInfo struct {

}
var (
    // 當前使用者含net.Conn的資訊
    CurUer model.CurUser
)

// 當前使用者含net.Conn的資訊
var CurUer model.CurUser
// 全域性變數 線上的好友使用者Id
var oneLineUser []int32

*使用者端連線伺服器 先進行登陸,登陸成功後:

1.實體化【當前使用者含net.Conn的資訊】

2.顯示當前所有好友 (這個資訊在登陸成功時返回)

3.使 用協程保持與伺服器連線 去迴圈讀取服務端發來訊息,根據不同的訊息型別做不同的處理:如:好友上線,私聊、群聊

4.迴圈讀取前端待發的訊息體,在迴圈體內通用Conn向伺服器傳送訊息

程式碼如下:

a. 客戶端Main方法 建入使用者名稱密碼,構建訊息實體

package main

import (
    "MySocket/Client/services"
    "MySocket/Commit/model"
    "encoding/json"
    "fmt"
)


func main()  {
    for {
        var user model.UserInfo
        var UserId, UserName, Pwd = 0, "", ""
        fmt.Println("請輸入UserId……")
        fmt.Scanln(&UserId)
        user.UserId = int32(UserId)
        fmt.Println("請輸入UserName……")
        fmt.Scanln(&UserName)
        user.UserName = UserName
        fmt.Println("請輸入Pwd……")
        fmt.Scanln(&Pwd)
        user.Pwd = Pwd
        // 向伺服器傳送訊息的結構體
        var sms model.SmsMessage
        byteUser, err := json.Marshal(user)
        if err != nil {
            fmt.Println("客戶端輸入使用者資訊轉Json出錯:", err)
            return
        }
        sms.Data = string(byteUser)
        // 訊息型別=2 表示登陸
        sms.Type = 2
        sendInfo := services.UserSmsProcessService{}
        // 呼叫這個方法進行登陸
        err = sendInfo.SendLogin(sms,user)
        if err != nil {
            fmt.Println("登陸失敗:", err)
        }
    }



}

b. sendInfo.SendLogin(sms,user) 向伺服器傳送登陸資訊,當登陸成功後,

1.實體化【當前使用者含net.Conn的資訊】

2.顯示當前所有好友 (這個資訊在登陸成功時返回)

3.使 用協程保持與伺服器連線 去迴圈讀取服務端發來訊息,根據不同的訊息型別做不同的處理:如:好友上線,私聊、群聊

4.迴圈讀取前端待發的訊息體,在迴圈體內通用Conn向伺服器傳送訊息

package services

import (
    "MySocket/Commit/model"
    "encoding/json"
    "fmt"
    "io"
    "net"
)
var (
    // 全域性變數 線上好友的使用者Id
    oneLineUser []int32
)

type UserSmsProcessService struct {
    // Conn net.Conn
}

// 使用者 登陸操作
func (this UserSmsProcessService) SendLogin(sms model.SmsMessage,user model.UserInfo) (err error){
    // 建立連線
    Conn,err :=net.Dial("tcp","127.0.0.1:8090")
    if err!=nil{
        fmt.Println("客戶端連線伺服器出錯……")
        return
    }
    defer Conn.Close()
    byteSms,err :=json.Marshal(sms)
    if err!=nil{
        fmt.Println("客戶端登陸資料序列化出錯:",err)
        return
    }
    sendInfo := &SmsReadWriteClientService{Conn: Conn}
    err= sendInfo.WritePkg(byteSms)
    if err!=nil{
        fmt.Println("客戶端登陸傳送到伺服器出錯:",err)
        return
    }
    // 獲取登陸返回資訊
    ReadInfo := &SmsReadWriteClientService{Conn: Conn}
    loginResultMsg,err := ReadInfo.ReadPkg()
    if err!=nil{
        fmt.Println("客戶端送接收登陸返回結果錯誤:",err)
        return
    }
     var response model.SmsResponse
    err = json.Unmarshal([]byte(loginResultMsg.Data),&response)
    if err!=nil{
        fmt.Println("客戶端送接收登陸返回結果返序列化錯誤:",err)
        return
    }
    if response.Code==200{
        fmt.Println("登陸200……")
// 1.實體化【當前使用者含net.Conn的資訊】 CurUer.UserId
=user.UserId CurUer.UserName=user.UserName CurUer.Conn=Conn oneLineUser= make([]int32,0) // 將當前線上的使用者新增到客戶端 全域性 線上使用者上去 oneLineUser = append(oneLineUser, response.OnLineUser...) // 2.顯示當前線上資訊 showinfo :=&ShowInfo{} showinfo.ShowOnLineUer(oneLineUser) /// *************** 保留該方法與服務端保持連線 ******************* // 3.根據客戶端接收到不同的訊息做相應的處理 這裡特別重要 ******************************* go this.ServiceAndClientLine(Conn) for{ // 4. 然後根據需要 隨時向伺服器傳送想發的訊息 注意:這裡特別重要……************************** // 構造待發的訊息體 通過輸入的方式 smsWaitSend,err:= showinfo.ShowMenuAndEnterSendContext() if err!=nil{ fmt.Println("構建群聊或私聊包時出錯:",err) continue } // 傳送想要發的訊息 err= sendInfo.WritePkg(smsWaitSend) if err!=nil{ fmt.Println("傳送群聊或私聊出錯:",err) } } }else { fmt.Println("登陸500……") fmt.Println("登陸失敗") return } return } /// *************** 保留該方法與服務端保持連線 ******************* func (this *UserSmsProcessService) ServiceAndClientLine(Conn net.Conn){ readOrWrite :=&SmsReadWriteClientService{Conn: Conn} for { fmt.Println("客戶端正在等待服務端傳送訊息") sms,err := readOrWrite.ReadPkg() if err!=nil{ if err==io.EOF{ fmt.Println("與伺服器斷開了連結……") return } fmt.Println("客戶端正在等待服務端傳送訊息") continue } switch sms.Type { case 3: // 有新使用者上線 var modelStatus model.SmsLineStatus err:= json.Unmarshal([]byte(sms.Data),&modelStatus) if err!=nil{ fmt.Println("接收伺服器使用者上線狀態反序列化失敗:",err) continue } // 將新上線的使用者新增到 線上使用者列表中 oneLineUser = append(oneLineUser, modelStatus.UserId) // 重新顯示線上使用者列表 showinfo :=&ShowInfo{} showinfo.ShowOnLineUer(oneLineUser) case 4: // 接到到的是私聊資訊 var toOne model.SmsToOne err:= json.Unmarshal([]byte(sms.Data),&toOne) if err!=nil{ fmt.Println("接收伺服器使用者私聊資訊反序列化失敗:",err) continue } fmt.Println("使用者:",toOne.SendUserId,"對你發來私信:",toOne.Context) case 8: fmt.Println(sms.Data) default: } // 使用者自己也可以進行傳送資訊操作 //showInfo :=&ShowInfo{} //showInfo.ShowMenuAndEnterSendContext() } }

上面的客戶終端輸入構建訊息實體的方法及顯示線上使用者 見下:

package services

import (
    "MySocket/Commit/model"
    "encoding/json"
    "fmt"
)


type ShowInfo struct {

}

// 顯示當前線上的使用者
func (this *ShowInfo) ShowOnLineUer(users []int32){
    fmt.Println("當前線上使用者Start:")
    for _,v :=range users{
        fmt.Println(v)
    }
    fmt.Println("當前線上使用者End!")
}

/// 顯示聊天選單,根據自身需要建立傳送聊天訊息,準備傳送
func (this *ShowInfo) ShowMenuAndEnterSendContext() (byteSms []byte,err error){
    var codeEnter int32
    var userId int32
    var Context string
    var sms model.SmsMessage
    fmt.Println("-------4. 私聊訊息---------")
    fmt.Println("-------5. 群聊訊息---------")
    fmt.Scanln(&codeEnter)
    sms.Type=codeEnter
    if codeEnter==4{
         var toone model.SmsToOne
         toone.SendUserId=CurUer.UserId
        fmt.Println("-------輸入私聊人的UserId---------")
        fmt.Scanln(&userId)
         toone.ToUserId=userId
        fmt.Println("-------輸入私聊人內容---------")
        fmt.Scanln(&Context)
         toone.Context=Context
         byteToone,err:=json.Marshal(toone)
         if err!=nil{
             fmt.Println("私聊內容序列化出錯!",err)
             return this.ShowMenuAndEnterSendContext()
         }
         sms.Data=string(byteToone)

    }else  if codeEnter==5{
         var toall model.SmsToAll
         toall.SendUserId=CurUer.UserId
         fmt.Println("-------輸入群聊內容---------")
         fmt.Scanln(&Context)
         byteToall,err :=json.Marshal(toall)
        if err!=nil{
            fmt.Println("私聊內容序列化出錯!",err)
            return this.ShowMenuAndEnterSendContext()
        }
        sms.Data=string(byteToall)

    }else {
        fmt.Println("-------4. 私聊訊息或5. 群聊訊息---------")
        return this.ShowMenuAndEnterSendContext()
    }
    byteSms,err= json.Marshal(sms)
    if err!=nil{
        fmt.Println("構建聊天訊息體序列化時出錯:",err)
        return this.ShowMenuAndEnterSendContext()
    }
    return

}

服務端思路:

1.先監聽埠,迴圈接收客戶端連線,每個連線用一個協程來進行處理,

2.迴圈讀取各連線發來的資訊

3.根據發來不同型別的資料進行不同處理 ,如登陸 --不管成功與否---返回客戶端資訊(成功還要返回線上的好友資訊)

私聊資訊-----定位私聊使用者-------向其傳送私聊資訊

群聊資訊-----遍歷當前所有線上使用者-------分別傳送群聊資訊(注意傳送給各使用者的net.Conn使用*********特別注意)

伺服器收發資訊方法程式碼

package services

import (
    "MySocket/Commit/model"
    "encoding/binary"
    "encoding/json"
    "fmt"
    "net"
)

// 傳送和讀取資訊
type  SmsReadWriteService struct {
    Conn net.Conn
}

// 讀取客戶端發來的資訊
func (this *SmsReadWriteService) ReadPkg() (msg model.SmsMessage,err error){
    var Buf [8096]byte //這時傳輸時,使用緩衝
    fmt.Println("讀取客戶端傳送的資訊……")
    _,err =this.Conn.Read(Buf[:4])
    if err!=nil{
        fmt.Println("讀取客戶端送的位元組數出錯:",err,"……")
        return
    }
    var pkgLen uint32
    // 將讀取到的位元組陣列轉化成Uint32的資料長度
    pkgLen=binary.BigEndian.Uint32(Buf[0:4])
    // 讀取該長度的資料
    n,err :=this.Conn.Read(Buf[:pkgLen])
    if n!=int(pkgLen){
        fmt.Println("服務端讀取的位元組數與接收到的位元組數不一致^")
        return
    }
    if err!=nil{
        fmt.Println("服務端讀取資料錯誤 :",err,"……")
        return
    }
    err = json.Unmarshal(Buf[:pkgLen],&msg)
    if err!=nil{
        fmt.Println("服務端讀取的Buf[:pkgLen]返序列化成SmsMessage出錯:",err,"……")
    }
    return
}

func (this *SmsReadWriteService) WritePkg(data []byte) (err error){
    // 先發送一個長度給對方
    var Buf [8096]byte //這時傳輸時,使用緩衝
    var pkgLen uint32
    pkgLen =uint32(len(data))
    binary.BigEndian.PutUint32(Buf[0:4],pkgLen)
    // 傳送長度
    n,err :=this.Conn.Write(Buf[:4])
    if n!=4{
        fmt.Println("服務端寫入資料與傳送長度不一致^")
        return
    }
    if err!=nil{
        fmt.Println("服務端寫入的Buf[:pkgLen]出錯:",err,"……")
        return
    }
    // 傳送訊息本身
    n,err=this.Conn.Write(data)
    if n!=int(pkgLen) {
        fmt.Println("服務端寫入資料與傳送的【訊息體】長度不一致^")
        return
    }
    if err!=nil{
        fmt.Println("服務端寫入訊息體出錯:",err,"……")
        return
    }
    return

}

全域性變數

// 當前線上使用者Map集合    在init()方法裡實例化
OnLineUserMap map[int32]model.UserLineMsg

Main方法入口
1.先監聽埠,迴圈接收客戶端連線
package main

import (
    "MySocket/Service/services"
    "fmt"
    "net"
)

func ResponseClientRequest(conn net.Conn){
    defer conn.Close()
    processService :=&services.ProcessorService{}
    // 迴圈接收客戶端發來的資訊,然後根據訊息型別來做相應的處理
    processService.ProcessorRead(conn)
}

func main()  {
    fmt.Println("伺服器開始監聽127.0.0.1:8090!")
    listen,err :=net.Listen("tcp","127.0.0.1:8090")
    if err!=nil {
        fmt.Println("伺服器監聽127.0.0.1:8090失敗!")
        return
    }
    defer listen.Close()
    for{
        conn,errCon:= listen.Accept()
        if errCon!=nil{
            fmt.Println("有一個連線失敗……")
            continue
        }
        fmt.Println("客戶端連線成功……")
        go ResponseClientRequest(conn)
    }

}
每個連線用一個協程來進行處理,迴圈讀取各連線發來的資訊
package services

import (
    "MySocket/Commit/model"
    "fmt"
    "io"
    "net"
)
var (
    // 當前線上使用者Map集合
    OnLineUserMap map[int32]model.UserLineMsg
)
func init(){
    // 初始化當前  當前線上使用者Map集合
    OnLineUserMap=make(map[int32]model.UserLineMsg)
}

type ProcessorService struct {
    // Conn net.Conn
}

// 迴圈讀取客戶端傳送過來的資訊
func (this *ProcessorService)  ProcessorRead(Conn net.Conn) (err error){
    // 迴圈讀取客戶端傳送過來的資訊
    for{
        read :=&SmsReadWriteService{
            Conn: Conn,
        }
        msg,err :=read.ReadPkg()
        if err!=nil{
            if err==io.EOF{
                fmt.Println("客戶端退出,伺服器端也退出..")
                return err
            }else {
                fmt.Println("readPkg err=", err)
                return err
            }
        }
        // 根據服務端接收的訊息型別來做相應處理
        err =this.ProcessorReadMsg(&msg,Conn)
        if err!=nil{
            return err
        }
    }

}

// 根據服務端接收的訊息型別來做相應處理
func (this *ProcessorService) ProcessorReadMsg(msg *model.SmsMessage,Conn net.Conn) (err error){

    //看看是否能接收到客戶端傳送的群發的訊息
    fmt.Println("mes=", msg)
    // 訊息 型別  1 註冊   2登陸  3線上狀態  4私聊資訊   5群發信息
    switch msg.Type {
    case 1: // 處理註冊資訊
    case 2:
        one := &UserMsgProcessService{ // 使用者資訊處理
            Conn: Conn,
        }
        // 傳遞上線資訊
        one.LoginInfo(msg)
    case 3: // 3線上狀態 息
    case 4:         //4私聊資訊
        toone := &UserMsgProcessService{ // 使用者資訊處理
             Conn: Conn,
        }
        toone.SendToOneMsg(msg)
    case 5: // 5群發信息

    }


    return
}

3.根據發來不同型別的資料進行不同處理

package services

import (
    "MySocket/Commit/model"
    "encoding/json"
    "fmt"
    "net"
)

// 使用者訊息處理服務
type  UserMsgProcessService struct {
    Conn net.Conn
}


//// 處理登陸資訊       Start
// 處理客戶端傳送過來的登陸資訊
func (this *UserMsgProcessService) LoginInfo(msg *model.SmsMessage)  (err error){
    var login model.SmsLogin
    err =json.Unmarshal([]byte(msg.Data),&login)
    if err !=nil{
        fmt.Println("伺服器處理登陸時出錯:",err)
        return err
    }
    var smsResponse model.SmsResponse
    if login.Pwd=="123"{
        smsResponse.Code=200
        var model model.UserLineMsg
        model.UserId=login.UserId
        model.Conn=this.Conn
        OnLineUserMap[login.UserId]=model   // 將某人上線的資訊寫入到線上Map中
        userArr :=this.SendOneOnLine(login.UserId)   // 通知相關好友 當前使用者上線了,並返回當前線上的人的IduserArr
        smsResponse.OnLineUser=userArr
    } else {
        smsResponse.Code=500
        smsResponse.Error="密碼錯誤,請重新輸入……"
    }
    byteSms,err :=json.Marshal(smsResponse)
    if err!=nil{
        fmt.Println("伺服器處理返回資訊時smsResponse轉JSon出錯:",err)
        return err
    }
    var response model.SmsMessage
    response.Type=7
    response.Data=string(byteSms)
    // 向客戶端傳送登陸是否成功的 狀態碼和錯誤 資訊
    err = this.SendLoginOverToClient(response)
    return err
}


// 伺服器告知相關好友,某人上線了  並返回當前線上的人的Id集userArr
func (this *UserMsgProcessService) SendOneOnLine(UserId int32) (userArr []int32){
    userArr=make([]int32,0)
    for _,v :=range OnLineUserMap{
        userArr = append(userArr,v.UserId )
        if v.UserId==UserId{
            continue
        }
        // 傳送格式下的  上下線 實體
        var lineStatus model.SmsLineStatus
        lineStatus.UserId=UserId   // 誰上線了
        lineStatus.Status=3   // 上線
        // 向v使用者傳送UserId 上線狀態 *****************************
        this.SendOneOnLineMsg(v,lineStatus)
    }
    return userArr
}

// 向某單個好友 傳送上線使用者的上線壯態或下線壯態  status=3 上線  status=6下線
// lineStatus model.SmsLineStatus   傳送的上下線實體
// lineStatus.UserId=UserId   // 誰上線了
// lineStatus.Status=3    // 上線3 下線 6
func (this *UserMsgProcessService) SendOneOnLineMsg(u model.UserLineMsg,lineStatus model.SmsLineStatus){

    byteStatus,err :=json.Marshal(lineStatus)
    if err!=nil{
        fmt.Println("伺服器處理byteStatus傳送使用者上線狀態時出錯:",err)
        return
    }
    // 傳送訊息實體
    var model model.SmsMessage
    // 訊息型別 為上下線狀態
    model.Type=lineStatus.Status
    model.Data=string(byteStatus)
    byteData,err :=json.Marshal(model)
    if err !=nil{
        fmt.Println("伺服器處理byteData傳送使用者上線狀態時出錯:",err)
        return
    }
    //建立訊息讀寫服務例項
    sendInfo :=&SmsReadWriteService{
        Conn: u.Conn,   // 注意  這裡的Conn 是要發給誰的Conn *****************
    }
    // 向當前u使用者傳送某使用者上下線訊息
    sendInfo.WritePkg(byteData)
}

// 向客戶端傳送登陸是否成功的 狀態碼和錯誤 資訊
func (this *UserMsgProcessService) SendLoginOverToClient(model model.SmsMessage) (err error){
    byteModel,err :=json.Marshal(model)
    if err!=nil{
        fmt.Println("伺服器處理登陸結果轉Json時出錯:",err)
        return  err
    }
    sendInfo :=&SmsReadWriteService{
        Conn: this.Conn,
    }
    err= sendInfo.WritePkg(byteModel)
    return err

}

//// 處理登陸資訊       End


// 有客戶端傳送私聊資訊  處理
func (this UserMsgProcessService) SendToOneMsg(sms *model.SmsMessage) (err error){
    byteSms,err :=json.Marshal(sms)
    if err!=nil{
        fmt.Println("伺服器序列化私聊整體訊息時出錯:",err)
        return
    }
    var smsToOne model.SmsToOne
    err= json.Unmarshal([]byte(sms.Data),&smsToOne)
    if err!=nil{
        fmt.Println("伺服器返序列化私聊訊息對像時出錯:",err)
        return
    }
// 定義接收人是否存在
var userExists bool=false for _,v :=range OnLineUserMap{ if v.UserId==smsToOne.ToUserId{ userExists=true sendInfo :=&SmsReadWriteService{ Conn: v.Conn, // 注意這裡Conn的傳遞,不要傳錯***************** }
// 向私聊人傳送私聊訊息 err
=sendInfo.WritePkg(byteSms) if err!=nil{ fmt.Println("伺服器傳送私聊訊息時出錯:",err) } return } }
// 假如接收人不存在
if !userExists{ var errSms model.SmsErrorResponse errSms.Code=500 errSms.Error="找不到該使用者" sms.Type=8 byteErrContext,err:= json.Marshal(errSms) if err!=nil{ fmt.Println("伺服器發找不到私聊使用者返回內部資訊序列化出錯:",err) return err } sms.Data=string(byteErrContext) byteErrSms,err :=json.Marshal(sms) if err!=nil{ fmt.Println("伺服器發找不到私聊使用者返回整體資訊序列化出錯:",err) return err } sendInfo :=&SmsReadWriteService{ Conn: this.Conn, } err= sendInfo.WritePkg(byteErrSms) } return }