golang用TCP协议实现简单的聊天室

发布时间:2020-10-01 06:35:31 作者:ustb80
来源:网络 阅读:37634

通常聊天室的架构分为服务器端和客户端:

服务器端:
接受来自于客户端的连接请求并建立连接;
所有客户端的连接会放进连接池中,用于广播消息;

客户端:
连接服务器;
向服务器发送消息;
接收服务器的广播消息;

注意事项:
某一个客户端断开连接后需要从连接池中摘除,并不再接收广播消息;
某一个客户端断开连接后不能影响服务器端或别的客户端的连接;

详细的代码如下,文档看注释就好了,不再细说:

服务器:

server.go

package main

import (
    "net"
    "log"
    "fmt"
)

func main() {
    port := "9090"
    Start(port)
}

// 启动服务器
func Start(port string) {
    host := ":" + port

    // 获取tcp地址
    tcpAddr, err := net.ResolveTCPAddr("tcp4", host)
    if err != nil {
        log.Printf("resolve tcp addr failed: %v\n", err)
        return
    }

    // 监听
    listener, err := net.ListenTCP("tcp", tcpAddr)
    if err != nil {
        log.Printf("listen tcp port failed: %v\n", err)
        return
    }

    // 建立连接池,用于广播消息
    conns := make(map[string]net.Conn)

    // 消息通道
    messageChan := make(chan string, 10)

    // 广播消息
    go BroadMessages(&conns, messageChan)

    // 启动
    for {
        fmt.Printf("listening port %s ...\n", port)
        conn, err := listener.AcceptTCP()
        if err != nil {
            log.Printf("Accept failed:%v\n", err)
            continue
        }

        // 把每个客户端连接扔进连接池
        conns[conn.RemoteAddr().String()] = conn
        fmt.Println(conns)

        // 处理消息
        go Handler(conn, &conns, messageChan)
    }
}

// 向所有连接上的乡亲们发广播
func BroadMessages(conns *map[string]net.Conn, messages chan string) {
    for {

        // 不断从通道里读取消息
        msg := <-messages
        fmt.Println(msg)

        // 向所有的乡亲们发消息
        for key, conn := range *conns {
            fmt.Println("connection is connected from ", key)
            _, err := conn.Write([]byte(msg))
            if err != nil {
                log.Printf("broad message to %s failed: %v\n", key, err)
                delete(*conns, key)
            }
        }
    }
}

// 处理客户端发到服务端的消息,将其扔到通道中
func Handler(conn net.Conn, conns *map[string]net.Conn, messages chan string) {
    fmt.Println("connect from client ", conn.RemoteAddr().String())

    buf := make([]byte, 1024)
    for {
        length, err := conn.Read(buf)
        if err != nil {
            log.Printf("read client message failed:%v\n", err)
            delete(*conns, conn.RemoteAddr().String())
            conn.Close()
            break
        }

        // 把收到的消息写到通道中
        recvStr := string(buf[0:length])
        messages <- recvStr
    }
}

客户端:
client.go

package main

import (
    "net"
    "log"
    "fmt"
    "os"
)

func main() {
    Start(os.Args[1])
}

func Start(tcpAddrStr string) {
    tcpAddr, err := net.ResolveTCPAddr("tcp4", tcpAddrStr)
    if err != nil {
        log.Printf("Resolve tcp addr failed: %v\n", err)
        return
    }

    // 向服务器拨号
    conn, err := net.DialTCP("tcp", nil, tcpAddr)
    if err != nil {
        log.Printf("Dial to server failed: %v\n", err)
        return
    }

    // 向服务器发消息
    go SendMsg(conn)

    // 接收来自服务器端的广播消息
    buf := make([]byte, 1024)
    for {
        length, err := conn.Read(buf)
        if err != nil {
            log.Printf("recv server msg failed: %v\n", err)
            conn.Close()
            os.Exit(0)
            break
        }

        fmt.Println(string(buf[0:length]))
    }
}

// 向服务器端发消息
func SendMsg(conn net.Conn) {
    username := conn.LocalAddr().String()
    for {
        var input string

        // 接收输入消息,放到input变量中
        fmt.Scanln(&input)

        if input == "/q" || input == "/quit" {
            fmt.Println("Byebye ...")
            conn.Close()
            os.Exit(0)
        }

        // 只处理有内容的消息
        if len(input) > 0 {
            msg := username + " say:" + input
            _, err := conn.Write([]byte(msg))
            if err != nil {
                conn.Close()
                break
            }
        }
    }
}

测试方法:

编译server.go和client.go;
打开终端,启动server,默认会监听9090端口;
再打开多个终端,启动client,client启动命令:client 服务器IP:9090;
在client中输入字符并回车,可以看到别的终端都会收到消息;

推荐阅读:
  1. TODO:Golang语言TCP/UDP协议重用地址端口
  2. TCP协议小结

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

tcp 协议 golang

上一篇:详解易语言线程同步

下一篇:iOS 12中无法获取WiFi的SSID了?别慌看这里!

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》