go語言實現聊天伺服器

來源:互聯網
上載者:User

看了兩天 go 語言,是時候練練手了。
go 的 routine(常式) 和 chan(通道) 簡直是神器,實現多線程(在 go 裡準確的來說是 多常式)簡直不要太輕鬆。

於是動手碼了一個傻瓜版的黑框聊天器。

server 端:
監聽 TCP 串連;支援自訂用戶端命令;支援訊息分發;理論上支援廣播;...

package mainimport (    "fmt"    "net"    "io"    "strconv"    "time"    "strings")const (    NORMAL_MESSAGE = iota    LIST_MESSAGE)var clientSenders = make(map[string] chan string)func send (addr string, conn *net.Conn){    senderChan := clientSenders[addr]    for s := range senderChan{        (*conn).Write([]byte(s))    }}func sendUsersInfo(addr string){    senderChan := clientSenders[addr]    if nil != senderChan{        ls := strconv.Itoa(LIST_MESSAGE)        cs := strconv.Itoa(NORMAL_MESSAGE) + "已登入用戶端列表:\n"        i := 1        for k := range clientSenders{            a := ""            if k == addr {                a = "(我)"            }            cs = cs + strconv.Itoa(i) + ")"  + k + a + "\n"            ls += k + "\n"            i ++        }        cs += "發送訊息,可使用 1<-這是給1號用戶端的訊息\n(請使用英文以擷取最佳體驗)\n"        senderChan <- cs        time.Sleep(time.Millisecond * 300)        senderChan <- ls        // 發送格式化的列表        fmt.Println("已發送“登入使用者資訊”", addr)    } else{        fmt.Println("用戶端接受通道不存在", addr)    }}func serve (conn *net.Conn){    connect := *conn    addr := connect.RemoteAddr().String()    fmt.Println(addr, "接入服務")    senderChan := make(chan string, 3)    clientSenders[addr] = senderChan    // 啟動發送    go send(addr, conn)    // 發送目前使用者資訊    go sendUsersInfo(addr)    buff := make([]byte, 10240)    for {        n, err := connect.Read(buff)        if err != nil {            if err == io.EOF {                fmt.Println("用戶端取消連結,", addr)                delete(clientSenders, addr)                return            } else{                fmt.Println(err)            }        }        msg := string(buff[:n])        // 重新整理用戶端列表        if msg == "ls\n" {            go sendUsersInfo(addr)            continue        }        // 提取資料        msgs := strings.Split(msg, "<-")        if len(msg) < 2{            senderChan <- string("資料格式不正確,請聯絡開發人員")            continue        }        aimAddr := msgs[0]        aimSender := clientSenders[aimAddr]        if aimSender == nil {            senderChan <- string("用戶端已下線,使用 ls 命令擷取最新的用戶端列表")            continue        }        aimSender <- strconv.Itoa(NORMAL_MESSAGE) + "[from:" + addr + "]:" + strings.Join(msgs[1:], "<-")    }}func main(){    addr := ":8080"    listener, err := net.Listen("tcp", addr)    if err != nil{        fmt.Println(err)        return    }    // 啟動訊息調度器    defer listener.Close()    // 啟動串連監聽    for {        conn, err := listener.Accept()        if err != nil {            fmt.Println(err)            continue        }        go serve(&conn)    }}

用戶端:
支援斷線重連;支援給特定其他用戶端發資訊

package mainimport (    "net"    "fmt"    "io"    "os"    "bufio"    "sync"    "time"    "strings"    "strconv")var conn *net.Connvar addrs []stringconst (    NORMAL_MESSAGE = iota    LIST_MESSAGE)func read(conn2 *net.Conn){    defer func() {        fmt.Println("嘗試重連")        go connectServer()    }()    connect := *conn2    buff := make([]byte, 20140)    for {        n, err := connect.Read(buff)        if err != nil {            if err == io.EOF{                fmt.Println("結束")                (*conn2).Close()                conn = nil                return            } else{                fmt.Println(err)            }        }        msg := string(buff[:n])        t, err := strconv.Atoi(string(msg[0]))        msg = msg[1:]        switch t {        case NORMAL_MESSAGE:            fmt.Print(msg)            break        case LIST_MESSAGE:            // 解析用戶端列表資料            addrs = strings.Split(msg, "\n")            fmt.Println("已接收用戶端列表。\n")            break        default:            fmt.Print(msg)            break        }    }}func connectServer(){    addr := "192.168.99.236:8080"    fmt.Println("等待伺服器開啟中")    conn2, err := net.Dial("tcp", addr)    if err != nil {        fmt.Print(err)        fmt.Println("串連失敗,10s後嘗試")        time.Sleep(10 * time.Second)        go connectServer()        return    }    fmt.Println("已串連")    conn = &conn2    go read(&conn2)}func send (){    inputReader := bufio.NewReader(os.Stdout)    for {        input, err := inputReader.ReadString('\n')        if err != nil {            if err == io.EOF{                return            } else{                fmt.Println(err)            }        }        if input == "ls\n" {            (*conn).Write([]byte(input))            continue        }        msgs := strings.Split(input, "<-")        if len(msgs) < 2 {            fmt.Println("發送的姿勢不正確,應該像這樣 1<-給1號發送訊息\n")            continue        }        index, err := strconv.Atoi(msgs[0])        if err != nil {            fmt.Println("發送的姿勢不正確,應該像這樣 1<-給1號發送訊息\n")            continue        }        if len(addrs) <= index {            fmt.Println("不存在第" + strconv.Itoa(index) + "個用戶端\n")            continue        }        addr := addrs[index-1]        input = addr + "<-" + strings.Join(msgs[1:], "<-")        if nil != conn {            (*conn).Write([]byte(input))        }    }}func main (){    var wg sync.WaitGroup    wg.Add(2)    go connectServer()    go send()    wg.Wait()    defer func() {        if nil != conn {            (*conn).Close()        }    }()}
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.