Golang实现简单tcp服务器03 -- 文本广播式聊天服务器/客户端

victoriest · · 3604 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

用Golang实现 文本广播式聊天服务器/客户端

本节, 我们将一步一步的把上一节完成的echo服务器/客户端改造成一个文本信息的聊天室

服务端的改动

  1. 服务器为了实现聊天信息的群体广播, 需要记录所有连接到服务器的客户端信息, 所以, 我们需要添加一个集合来保存所有客户端的连接:

    var ConnMap map[string]*net.TCPConn

  2. 接着, 每次当有新的客户端连接到服务器时, 需要把这个客户端连接行信息加入集合:

    ConnMap[tcpConn.RemoteAddr().String()] = tcpConn

  3. 当服务器收到客户端的聊天信息时, 需要广播到所有客户端, 所以我们需要利用上面保存TCPConn的map来遍历所有TCPConn进行广播, 用以下方法实现:

    func boradcastMessage(message string) {
        b := []byte(message)
        for _, conn := range ConnMap {
        conn.Write(b)
        }
    }
    

客户端代码改动

客户端代码改动相对简单, 只是加入了用户自己输入聊天信息的功能, 在连接成功并且 启动了消息接收的gorountine后, 加入以下代码:

    for {
       var msg string
       fmt.Scanln(&msg)
       if msg == "quit" {
         break
       }
       b := []byte(msg + "\n")
       conn.Write(b)
    }

完整的服务端代码如下:

server.go

package main

import (
    "bufio"
    "fmt"
    "net"
)

// 用来记录所有的客户端连接
var ConnMap map[string]*net.TCPConn

func main() {
    var tcpAddr *net.TCPAddr
    ConnMap = make(map[string]*net.TCPConn)
    tcpAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:9999")

    tcpListener, _ := net.ListenTCP("tcp", tcpAddr)

    defer tcpListener.Close()

    for {
       tcpConn, err := tcpListener.AcceptTCP()
       if err != nil {
         continue
       }

       fmt.Println("A client connected : " + tcpConn.RemoteAddr().String())
       // 新连接加入map
       ConnMap[tcpConn.RemoteAddr().String()] = tcpConn
       go tcpPipe(tcpConn)
    }

}

func tcpPipe(conn *net.TCPConn) {
    ipStr := conn.RemoteAddr().String()
    defer func() {
       fmt.Println("disconnected :" + ipStr)
       conn.Close()
    }()
    reader := bufio.NewReader(conn)

    for {
       message, err := reader.ReadString('\n')
       if err != nil {
         return
       }
       fmt.Println(conn.RemoteAddr().String() + ":" + string(message))
       // 这里返回消息改为了广播
       boradcastMessage(conn.RemoteAddr().String() + ":" + string(message))
    }
}


func boradcastMessage(message string) {
    b := []byte(message)
    // 遍历所有客户端并发送消息
    for _, conn := range ConnMap {
       conn.Write(b)
    }
}

客户端完整代码如下:

client.go

package main

import (
    "bufio"
    "fmt"
    "net"
)

func main() {
    var tcpAddr *net.TCPAddr
    tcpAddr, _ = net.ResolveTCPAddr("tcp", "127.0.0.1:9999")

    conn, _ := net.DialTCP("tcp", nil, tcpAddr)
    defer conn.Close()
    fmt.Println("connected!")

    go onMessageRecived(conn)

    // 控制台聊天功能加入
    for {
       var msg string
       fmt.Scanln(&msg)
       if msg == "quit" {
         break
       }
       b := []byte(msg + "\n")
       conn.Write(b)
    }
}

func onMessageRecived(conn *net.TCPConn) {
    reader := bufio.NewReader(conn)
    for {
       msg, err := reader.ReadString('\n')
       fmt.Println(msg)
       if err != nil {
         quitSemaphore <- true
         break
       }
    }
}

最后分别编译server与client试试效果吧!

go build server.go

go build client.go

先启动server端, 然后新开两个个终端, 启动客户端, 在其中一个客户端里键入聊天信息后回车, 会发现另外一个客户端收到了刚刚发送的聊下天信息

完事大吉!

入群交流(和以上内容无关):Go中文网 QQ 交流群:729884609 或加微信入微信群:274768166 备注:入群;关注公众号:Go语言中文网

3604 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传