1 回答

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")
}
- 1 回答
- 0 关注
- 142 浏览
添加回答
举报