1. 程式人生 > 其它 >go語言併發聊天室

go語言併發聊天室

小記一下

server端

package main

import (
    "encoding/json"
    "fmt"
    "net"
    "unsafe"
)

//傳輸的資料結構
type ChatMsg struct {
    From, To, Msg string //從哪裡來,到哪裡去,內容
}

//與客戶端通訊的結構
type ClientMsg struct {
    To      string  `json:"to"`       //去哪
    Msg     string  `jason:"msg"`     //內容
    Datalen uintptr `jason:"
datalen"` //資料長度 } //定義訊息中心用的channel var chan_msgcenter chan ChatMsg //定義使用者名稱到ip的map var mapName2CliAddr map[string]string //定義ip到conn的map var mapCliaddr2Clients map[string]net.Conn //主函式 func main() { //初始化 mapCliaddr2Clients = make(map[string]net.Conn) mapName2CliAddr = make(map[string]string
) chan_msgcenter = make(chan ChatMsg) //監聽 lsner, err := net.Listen("tcp", "localhost:8888") if err != nil { panic("listening failed!") } defer lsner.Close() //開一個routine起資訊中心 go Msg_center() //迴圈接收訊息 for { conn, err := lsner.Accept() if err != nil { fmt.Println(
"ACCEPT FAILED!") break } //開一個routine處理訊息 go Handle_conn(conn) } } //處理斷開連線 func Logout(conn net.Conn, from string) { defer conn.Close() //在連線map裡刪掉 delete(mapCliaddr2Clients, from) //告訴訊息中心,發給大家退出登入的訊息 msg := ChatMsg{from, "all", from + "->logout"} chan_msgcenter <- msg } func Msg_center() { for { //等訊息傳過來,有了就發 msg := <-chan_msgcenter go send_msg(msg) } } //處理新連線 func Handle_conn(conn net.Conn) { //獲取ip from := conn.RemoteAddr().String() //在map裡新增鍵值對 ip->連線 mapCliaddr2Clients[from] = conn //告訴所有人登陸了 msg := ChatMsg{from, "all", from + "->login"} chan_msgcenter <- msg defer Logout(conn, from) //讀取資訊 buf := make([]byte, 256) for { n, err := conn.Read(buf) if err != nil { fmt.Println("reading failed!") break } if n > 0 { //將資訊jsondecode後放進climsg結構體 var climsg ClientMsg err := json.Unmarshal(buf[:n], &climsg) if err != nil { fmt.Println("unmarshaling failed!") continue } //檢驗格式 if climsg.Datalen != unsafe.Sizeof(climsg) { fmt.Println("unvalid format!", climsg) continue } //預設傳送物件為all chatmsg := ChatMsg{from, "all", climsg.Msg} //判斷髮到哪裡,可以通過set來設定使用者名稱 switch climsg.To { case "all": case "set": mapName2CliAddr[climsg.Msg] = from chatmsg.Msg = from + " set name=" + climsg.Msg + "success" chatmsg.From = "server" default: chatmsg.To = climsg.To } //傳給channel chan_msgcenter <- chatmsg } } } func send_msg(msg ChatMsg) { //先jsonencode一下 data, err := json.Marshal(msg) if err != nil { fmt.Println("marshaling failed!") return } //判斷髮給誰 if msg.To == "all" { for _, v := range mapCliaddr2Clients { //不給自己發 if msg.From != v.RemoteAddr().String() { v.Write(data) } } } else { //私聊,判斷使用者存在和是否線上 from, ok := mapName2CliAddr[msg.To] if !ok { fmt.Println("USER not EXISTS!", msg.To) return } conn, ok := mapCliaddr2Clients[from] if !ok { fmt.Println("cli not exists!", from, msg.To) return } conn.Write(data) } }

 

client端

package main

import (
    "bufio"
    "encoding/json"
    "fmt"
    "net"
    "os"
    "strings"
    "unsafe"
)

//與客戶端通訊的結構
type ClientMsg struct {
    To      string  `json:"to"`       //去哪
    Msg     string  `jason:"msg"`     //內容
    Datalen uintptr `jason:"datalen"` //資料長度
}

//使用者幫助
func Help() {
    fmt.Println("set: your name")
    fmt.Println("all: your msg -- broadcadt")
    fmt.Println("anyone: your msg -- private msg")
}
func main() {
    //連線
    conn, err := net.Dial("tcp", "loaclhost:8888")
    if err != nil {
        panic("dialing failed!")
    }
    defer conn.Close()
    //通訊
    go handle_conn(conn)
    //讀標準輸入
    reader := bufio.NewReader(os.Stdin)
    fmt.Printf("Welcome to YU's pub chat\n")
    Help()
    for {
        fmt.Printf("YU's chat>")
        msg, err := reader.ReadString('\n')
        if err != nil {
            panic("reading string failed!")
        }
        msg = strings.Trim(msg, "\r\n")
        if msg == "quit" {
            fmt.Println("bye~")
            break
        }
        if msg == "help" {
            Help()
            continue
        }
        //訊息傳輸格式處理
        msgs := strings.Split(msg, ":")
        if len(msgs) == 2 {
            var climsg ClientMsg
            climsg.To = msgs[0]
            climsg.Msg = msgs[1]
            climsg.Datalen = unsafe.Sizeof(climsg)
            data, err := json.Marshal(climsg)
            if err != nil {
                fmt.Println("marshaling failed", err, climsg)
                continue
            }
            _, err = conn.Write(data)
            if err != nil {
                fmt.Println("writing failed", err, data)
                break
            }
        }
    }
}
func handle_conn(conn net.Conn) {
    buf := make([]byte, 256)
    for {
        n, err := conn.Read(buf)
        if err != nil {
            panic("reading failed!")
        }
        fmt.Println(string(buf[:n]))
        fmt.Printf("YU's chat >")
    }
}