1 回答

TA貢獻1869條經(jīng)驗 獲得超4個贊
免責(zé)聲明,可能有更好的方法來做到這一點,但這是我想出的。
主要挑戰(zhàn)是關(guān)閉所有連接。這具有挑戰(zhàn)性的原因是因為我們只能在不等待Read
網(wǎng)絡(luò)連接的情況下檢查通道。默認(rèn)情況下,網(wǎng)絡(luò)連接上的讀取將始終阻塞,直到發(fā)送數(shù)據(jù)或從另一端關(guān)閉它們。
我們可以使用readDeadline在設(shè)定的時間返回網(wǎng)絡(luò)連接的讀取。這允許我們檢查通道是否仍然每 x 秒打開一次,如果我們在每次超過期限時都繼續(xù)延長期限。
現(xiàn)在我們可以關(guān)閉共享通道來終止所有連接。這將占用配置的超時時間。另請注意,一旦頻道關(guān)閉,您將無法重新打開它,如果您想在“退出”命令后接受連接,則必須創(chuàng)建一個新頻道。如果您想將此作為正常關(guān)閉的一部分,則無需執(zhí)行此操作,并且可以忽略重置退出通道的 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 關(guān)注
- 155 瀏覽
添加回答
舉報