如何编写在goroutine中执行的函数以发送和接收消息?

时间:2019-04-09 15:32:59

标签: go networking tcp websocket server

我必须编写一个客户端代码,该代码从服务器接收字符串形式的消息,并从控制台接收输入,以将消息结束到服务器。这两个操作应彼此同时运行。我已经编写了执行这些操作的代码,但是没有顺序执行。

这是我当前的代码:

func SocketClient() {
    conn, err := net.Dial("tcp", ":9000")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()
    server_reader := bufio.NewReader(conn)
    input_reader := bufio.NewReader(os.Stdin)
    for {
        // for sending messages
        buff, err := input_reader.ReadString('\n')
        if err != nil {
            log.Fatalln(err)
        }
        conn.Write([]byte(buff))

        // for receiving messages but this won't run until the above has taken input from console
        buff2, err := server_reader.ReadString('\n')
        if err != nil {
            log.Fatalln(err)
        }
        fmt.Println("Received: %s", buff2)
    }
}

buff从服务器接收传入消息,然后buff2从控制台接收传出消息输入,但是为了再次接收传入消息,buff2需要一些输入。我知道可以使用通道,互斥锁等来完成此操作,但是由于我对基本原理缺乏了解,因此在使用它们时遇到了问题。

我想实际的代码应该像这样:

func SocketClient() {
    conn, err := net.Dial("tcp", ":9000")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()
    go func() {
        // for sending messages
    } ()
    go func() {
        // for receiving messages
    } ()
}

如何使输入和输出成为两个独立的goroutine?

3 个答案:

答案 0 :(得分:1)

直接在SocketClient中运行循环之一。在以SocketClient开头的新goroutine中运行另一个循环。

func SocketClient() error {
    conn, err := net.Dial("tcp", ":9000")
    if err != nil {
        return err
    }
    defer conn.Close()
    server_reader := bufio.NewReader(conn)
    input_reader := bufio.NewReader(os.Stdin)

    go func() {
        defer conn.Close()

        // This loop will break and the goroutine will exit when the
        // SocketClient function executes conn.Close().

        for {
            buff, err := server_reader.ReadBytes('\n')
            if err != nil {
                // optional: log.Fatal(err) to cause program to exit without waiting for new input from stdin.
                return
            }
            fmt.Printf("Received: %s", buff)
        }
    }()

    for {
        // for sending messages
        buff, err := input_reader.ReadBytes('\n')
        if err != nil {
            return err
        }
        if _, err := conn.Write(buff); err != nil {
            return err
        }
    }
}

使用bufio.Reader ReadBytes方法代替ReadString,以避免在[]bytestring之间进行不必要的转换。

答案 1 :(得分:0)

您可以创建一个像这样的函数,该函数返回一个通道。它还会启动一个新的go例程,该例程将写入返回的通道。

这允许使用者调用此方法,并期望值返回到返回的通道上。

我还更新了方法签名以使用<form method="post" enctype="multipart/form-data"> <input type="hidden" id="formUpload" name="formUpload"> <input type="file" name="formUploadFile" required> <label for="ds" >Hiermit willige ich ein, dass die eingegebenen Daten zur Bearbeitung meines Anliegens gespeichert und verarbeitet werden. Folgende Daten werden an die EK Frachtenservice übermittelt: Alle Daten welche im Formular angegeben sind. <input type="checkbox" id="ds" name="ds" required> <span class="checkmark"></span> </label> <input type="submit" value="Antragsformulare hochladen" name="submit" id="send"> </form> ,因为它是在换行符上分割的更有效方法。

*bufio.Scanner

现在我们可以在func ReadFully(scanner *bufio.Scanner) <-chan string { out := make(chan string) go func() { // once all the values have been read, close the channel // this tells the consumers that there will not be anymore // values and they don't need to keep listening to this channel. defer close(out) for scanner.Scan() { out <- scanner.Text() } if err := scanner.Err(); err!= nil { log.Fatal(err) } }() return out } 中使用此ReadFully方法

SocketClient

注意,此示例完全省略了取消等操作。通常,如果没有停止它的方法,切勿启动go例程。

查看有关管道的出色文章。 https://blog.golang.org/pipelines

答案 2 :(得分:0)

我写了一篇关于Go并发的博客文章。 https://marcofranssen.nl/concurrency-in-go/

还使用了此博客文章中的一些概念。

https://marcofranssen.nl/go-webserver-with-gracefull-shutdown/

这还使我能够拦截命令行输入(例如信号)以安全地关闭服务器。

您可以使用类似的方法来获取您的命令输入并进行使用。

主要思想是使用通道在go例程之间进行通信。