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

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

这是我当前的代码:</ p>

  func SocketClient(  ){
conn,err:= net.Dial(“ tcp”,“:9000”)
if err!= nil {
log.Fatal(err)
}
延迟conn.Close()\ n server_reader:= bufio.NewReader(conn)
input_reader:= bufio.NewReader(os.Stdin)
for {
//用于发送消息
buff,err:= input_reader.ReadString('
')

if err!= nil {
log.Fatalln(err)
}
conn.Write([] byte(buff))

//用于接收消息,但这要等到上面 已从控制台获取输入
buff2,错误:= server_reader.ReadString('
')
如果错误!= nil {
log.Fatalln(err)
}
fmt.Println(“接收:% s“,buff2)
}
}
</ code> </ pre>

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

我想实际的代码应该看起来像这样 :</ p>

  func SocketClient(){
conn,err:= net.Dial(“ tcp”,“:9000”)
如果err!= nil {
log.Fatal(err)
}
延迟conn.Close()
go func(){
//用于发送消息
}()
go func(){
//用于接收消息
}()
}
</ code> </ pre>

如何将输入和输出作为两个独立的goroutine?</ p>
</ div>

展开原文

原文

I have to write a client side code that is receiving messages in the form of strings from server as well as taking input from the console to end message to the server. Both these operations should run concurrently with each other. I have written a code which perform these operations but sequentially not concurrently.

Here's what my current code looks like:

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('
')
        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('
')
        if err != nil {
            log.Fatalln(err)
        }
        fmt.Println("Received: %s", buff2)
    }
}

buff receives incoming messages from server and then buff2 takes outgoing message input from console but in order to receive the incoming messages again, buff2 needs some input. I know this can be done using channels, mutex locks etc. but because of my lack of understanding of fundamentals I am having problem using them.

I guess the actual code should look something like this:

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
    } ()
}

How to make the input and output as two separate goroutines?

douzhuochao4027
douzhuochao4027 关于术语的注释:您不编写goroutine。您编写在goroutines中执行的函数。将goroutine视为线程。
一年多之前 回复
dongshui9690
dongshui9690 互斥锁用于同步访问某些共享数据,因此不适用于此处。将它们分开看起来就像您写出的一样:一个例程从conn读取并打印到stdout,一个例程从stdin读取并写入conn;并且您已经具有执行这些操作的代码。您是否尝试过以这种方式实际实施?您遇到了什么问题?
一年多之前 回复

3个回答



直接在 SocketClient </ code>中运行循环之一。 在由 SocketClient </ code>开始的新goroutine中运行另一个循环。</ p>

  func SocketClient()错误{
conn,err:= net.Dial( “ tcp”,“:9000”)
如果err!= nil {
返回err
}
延迟conn.Close()
server_reader:= bufio.NewReader(conn)
input_reader:= bufio。 NewReader(os.Stdin)

go func(){
延迟conn.Close()

//当
// SocketClient函数执行conn.Close时,此循环将中断并且goroutine将退出 ()。

for {
buff,err:= server_reader.ReadBytes('
')
if err!= nil {
//可选:log.Fatal(err)导致程序退出
return
}
fmt.Printf(“ Received:%s”,buff)
}
}()

for {
//发送消息
buff,错误:= input_reader.ReadBytes('
')
如果错误!= nil {
return err
}
如果_,错误: = conn.Write(buff); err!= nil {
return err
}
}
}
</ code> </ pre>

请改用bufio.Reader ReadBytes </ code>方法 ReadString </ code>的代码,以避免在 [] byte </ code>和 string </ code>之间进行不必要的转换。</ p>
</ div>

展开原文

原文

Run one of the loops directly in SocketClient. Run the other loop in a new goroutine started by SocketClient.

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('
')
            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('
')
        if err != nil {
            return err
        }
        if _, err := conn.Write(buff); err != nil {
            return err
        }
    }
}

Use the bufio.Reader ReadBytes method instead of the ReadString to avoid unnecessary conversions between []byte and string.

douqi3913
douqi3913 goroutine在SocketClient函数中启动。 它循环读取行,直到读取返回错误。 读取块,直到读取一条线或连接出现错误。
一年多之前 回复
dongni9825
dongni9825 您的代码可以正常工作,但是您能告诉我您在goroutine中编写的函数何时恰好有机会被执行? 一旦执行在goroutine之后到达for循环,它便会连续运行,因为该函数中没有Sleep,chan,Waitgroup。 我看到的所有其他教程都涉及到使用它们来实现您在此处所做的工作。
一年多之前 回复

You can create a function like this one, that returns a channel. It also starts a new go routine that will write to the returned channel.

This allows consumers to call this method, and expect values back on the returned channel.

I've also updated the method signature to use a *bufio.Scanner as it's a more efficient method of splitting on newlines.

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
}

Now we can use this ReadFully method in SocketClient

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

    serverScan := bufio.NewScanner(conn)
    inputScan := bufio.NewScanner(os.Stdin)


    serverCh := ReadFully(serverScan)
    inputCh := ReadFully(inputScan)


    // create a wait group, this stops the SocketClient 
    // method from exiting until the workers are done.
    var wg sync.WaitGroup
    wg.Add(2)

    go func() {
        defer wg.Done()
        for v := range inputCh {
            _, _ = conn.Write([]byte(v))
        }
    }()

    go func() {
        defer wg.Done()
        for v := range serverCh {
            fmt.Printf("Received: %s
", v)
        }
    }()

    wg.Wait()
}

Note, this example entirely omits cancellations etc. In general you should never start a go routine without a way to stop it.

Check out this great article on pipelines. https://blog.golang.org/pipelines



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

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

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

,这也使我可以拦截命令行输入 像信号一样可以安全地关闭服务器。</ p>

您可能会使用类似的方法来获取命令输入并对其进行使用。</ p>

主要思想是 使用通道在go例程之间进行通信。</ p>
</ div>

展开原文

原文

I wrote a blog post on Go concurrency. https://marcofranssen.nl/concurrency-in-go/

also used some of the concepts in this blog post.

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

which also allows me to intercept commandline input like signals to safely shutdown the server.

You might use a similar approach to take your commanding input and user it.

The main idea is to use channels for communicating between your go routines.

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问
相关内容推荐