为了账号安全,请及时绑定邮箱和手机立即绑定

在 Go 中关闭客户端服务器通信

在 Go 中关闭客户端服务器通信

Go
慕桂英3389331 2022-10-17 10:09:51
我创建了这个服务器,它通过将它连接到端口 8080 与 PuTTY 客户端发送该服务器接收的数据。现在我想用频道关闭所有东西,我该怎么做?写完“退出”。全部用 Golang 编写。package mainimport (    "bufio"    "fmt"    "net")func main() {    //Ascolta richiesta    datastream, err := net.Listen("tcp", ":8080")    if err != nil {        fmt.Println(err)        return    }    defer datastream.Close()    //Accetta richiesta    for {        connessione, err := datastream.Accept()        if err != nil {            fmt.Println(err)            return        }        go handle(connessione)    }}//Connessione Handle > Threadfunc handle(connessione net.Conn) {    scanner := bufio.NewScanner(connessione)    for scanner.Scan() {        data := scanner.Text()        fmt.Printf("%q\n", data)        if data == "exit" {            connessione.Close()        }    }    if err := scanner.Err(); err != nil {        fmt.Println("error", err)    }}
查看完整描述

1 回答

?
MMTTMM

TA贡献1869条经验 获得超4个赞

免责声明,可能有更好的方法来做到这一点,但这是我想出的。

主要挑战是关闭所有连接。这具有挑战性的原因是因为我们只能在不等待Read网络连接的情况下检查通道。默认情况下,网络连接上的读取将始终阻塞,直到发送数据或从另一端关闭它们。

我们可以使用readDeadline在设定的时间返回网络连接的读取。这允许我们检查通道是否仍然每 x 秒打开一次,如果我们在每次超过期限时都继续延长期限。

现在我们可以关闭共享通道来终止所有连接。这将占用配置的超时时间。另请注意,一旦频道关闭,您将无法重新打开它,如果您想在“退出”命令后接受连接,则必须创建一个新频道。如果您想将此作为正常关闭的一部分,则无需执行此操作,并且可以忽略重置退出通道的 goroutine。

我们最终得到类似的东西:

package main


import (

    "bufio"

    "errors"

    "fmt"

    "io"

    "net"

    "os"

    "time"

)


func main() {


    //Ascolta richiesta

    datastream, err := net.Listen("tcp", ":8080")


    if err != nil {

        fmt.Println(err)

        return

    }

    defer datastream.Close()


    // Make a channel on which we can't send any data, just close it

    exit := make(chan struct{})


    // Replace the closed channel with a new channel so we can accept new connection again. (optional)

    go func() {

        for {

            <-exit

            exit = make(chan struct{})

            fmt.Println("recreate chan")

        }

    }()


    //Accetta richiesta

    for {

        connessione, err := datastream.Accept()

        if err != nil {

            fmt.Println(err)

            return

        }


        // Give the channel to every connection

        go handle(connessione, exit)

    }


}


//Connessione Handle > Thread

func handle(connessione net.Conn, exit chan struct{}) {


    // Set the read timeout, this will cause the connection to return an os.ErrDeadlineExceeded error

    // every 5 seconds if no data read in that time.

    timeoutEvery := 5 * time.Second

    err := connessione.SetReadDeadline(time.Now().Add(timeoutEvery))

    if err != nil {

        fmt.Println("error", err)

    }


    // This pipe will allow us to copy data from the Network connection to the scanner without the scanner

    // via this goroutine.

    r, w := io.Pipe()

    // Close the pipeReader after we are done

    defer func() {

        r.Close()

    }()

    go func() {

        // Close the pipe and network connection when returning

        defer func() {

            fmt.Println("connection closed")

            connessione.Close()

            w.Close()

        }()


        // Allocate a buffer for the copy

        b := make([]byte, 32*1024)

        for {

            select {

            case <-exit:

                // If exit has been closed, we will enter this case

                return

            default:

                // If exit is still open, we enter this case.


                // Copy data from the connection to the pipe writer, use CopyBuffer to avoid temporary

                // buffer allocation(speed improvement)

                _, err := io.CopyBuffer(w, connessione, b)

                if err != nil {

                    // If an error is returned, check if this is due to the read deadline

                    if errors.Is(err, os.ErrDeadlineExceeded) {

                        // If it is, just extend it by our timeout again

                        err := connessione.SetReadDeadline(time.Now().Add(timeoutEvery))

                        if err != nil {

                            fmt.Println("error", err)

                            return

                        }


                        continue

                    }


                    // If there is any other error, close the connection.


                    fmt.Println("error", err)

                    return

                }

            }

        }

    }()


    scanner := bufio.NewScanner(r)


    for scanner.Scan() {

        data := scanner.Text()

        fmt.Printf("%q\n", data)

        if data == "exit" {

            // Close the exit channel, this will cause all goroutines to close the network connections

            // and the handlers to exit.

            close(exit)

        }


    }


    if err := scanner.Err(); err != nil {

        fmt.Println("error", err)

    }


    fmt.Println("handle return")

}


查看完整回答
反对 回复 2022-10-17
  • 1 回答
  • 0 关注
  • 142 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号