英文:
What is causing the "Connection reset by peer" error in the Go server?
问题
我已经用中文翻译了你提供的内容,请查看以下翻译结果:
我已经用Go语言编写了一个基本的Web服务器,但它似乎无法正确处理任何请求,而且我只得到了"Recv failure: Connection reset by peer"的错误信息。
我很高兴服务器确实能够正确启动和停止,我可以看到它在我配置的8080端口上监听。我也排除了解析YAML配置文件的问题 - 它确实被解析成了http.Server{}。
我不太确定还应该检查什么,也很难找到任何指向正确方向的线索。
我提前道歉,因为我知道我要粘贴的代码量很大,但我真的不知道错误出在哪里。
考虑到服务器正在运行,当我访问"/"端点/路由时,我期望返回"Hello from Go!"。
目录结构如下:
❯ tree . | grep -iE 'cmd|server.go|go.mod|go.sum|server.yaml'
├── cmd
│   └── server.go
├── go.mod
├── go.sum
└── server.yaml
server.go的代码如下:
package main
import (
	"context"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
	"gopkg.in/yaml.v3"
)
type Config struct {
	Port            string `yaml:"Port"`
	ReadTimeout     int    `yaml:"ReadTimeout"`
	WriteTimeout    int    `yaml:"WriteTimeout"`
	IdleTimeout     int    `yaml:"IdleTimeout"`
	ShutdownTimeout int    `yaml:"ShutdownTimeout"`
	ErrorLog        string `yaml:"ErrorLog"`
}
func main() {
	if len(os.Args) != 2 {
		log.Fatal("Missing arguments")
	}
	configFile, err := os.Open(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}
	defer configFile.Close()
	// TODO: Implement a custom ServeMux + routes
	serverConfig := Config{}
	yamlDecoder := yaml.NewDecoder(configFile)
	err = yamlDecoder.Decode(&serverConfig)
	if err != nil {
		log.Fatal(err)
	}
	errorLogFile, err := os.OpenFile(serverConfig.ErrorLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatal(err)
	}
	defer errorLogFile.Close()
	errorLog := log.New(errorLogFile, "ERROR : ", log.LstdFlags|log.Lshortfile)
	server := &http.Server{
		Addr:         fmt.Sprintf(":%s", serverConfig.Port),
		ReadTimeout:  time.Duration(serverConfig.ReadTimeout),
		WriteTimeout: time.Duration(serverConfig.WriteTimeout),
		IdleTimeout:  time.Duration(serverConfig.IdleTimeout),
		ErrorLog:     errorLog,
	}
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hello from Go!")
	})
	log.Println("Starting the server...")
	go func() {
		err := server.ListenAndServe()
		if !errors.Is(err, http.ErrServerClosed) {
			log.Fatal(err)
		}
		log.Println("Stopped serving new connections")
	}()
	log.Println("Server started")
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
	<-sigChan
	ctx, shutdown := context.WithTimeout(context.Background(), time.Duration(serverConfig.ShutdownTimeout)*time.Second)
	defer shutdown()
	err = server.Shutdown(ctx)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Server gracefully stopped")
}
错误信息如下:
❯ curl -v localhost:8080/
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
* Recv failure: Connection reset by peer
* Closing connection 0
curl: (56) Recv failure: Connection reset by peer
希望这些信息对你有帮助!如果你有任何其他问题,请随时问我。
英文:
I have written a basic web server in Go, but it can't seem to handle any requests properly and instead all I get is "Recv failure: Connection reset by peer".
I'm happy that the server does indeed start and stop correctly, and I can see it listening on port 8080 that I've configured. I've also ruled out issues with parsing the YAML config file - it's definitely getting getting parsed in to http.Server{}.
I'm not really sure what else to check, and struggling to find anything that's pointing me in the right direction.
I'll also apologise in advance, as I know I'm pasting a large amount of code below, but I really don't know what and where the error is coming from.
Given that the server is running, when I hit the "/" endpoint/route, I'd expect to get "Hello from Go!" returned back.
directory structure
❯ tree . | grep -iE 'cmd|server.go|go.mod|go.sum|server.yaml'
├── cmd
│   └── server.go
├── go.mod
├── go.sum
└── server.yaml
server.go
package main
import (
	"context"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
	"gopkg.in/yaml.v3"
)
type Config struct {
	Port            string `yaml:"Port"`
	ReadTimeout     int    `yaml:"ReadTimeout"`
	WriteTimeout    int    `yaml:"WriteTimeout"`
	IdleTimeout     int    `yaml:"IdleTimeout"`
	ShutdownTimeout int    `yaml:"ShutdownTimeout"`
	ErrorLog        string `yaml:"ErrorLog"`
}
func main() {
	if len(os.Args) != 2 {
		log.Fatal("Missing arguments")
	}
	configFile, err := os.Open(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}
	defer configFile.Close()
	// TODO: Implement a custom ServeMux + routes
	serverConfig := Config{}
	yamlDecoder := yaml.NewDecoder(configFile)
	err = yamlDecoder.Decode(&serverConfig)
	if err != nil {
		log.Fatal(err)
	}
	errorLogFile, err := os.OpenFile(serverConfig.ErrorLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatal(err)
	}
	defer errorLogFile.Close()
	errorLog := log.New(errorLogFile, "ERROR : ", log.LstdFlags|log.Lshortfile)
	server := &http.Server{
		Addr:         fmt.Sprintf(":%s", serverConfig.Port),
		ReadTimeout:  time.Duration(serverConfig.ReadTimeout),
		WriteTimeout: time.Duration(serverConfig.WriteTimeout),
		IdleTimeout:  time.Duration(serverConfig.IdleTimeout),
		ErrorLog:     errorLog,
	}
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hello from Go!")
	})
	log.Println("Starting the server...")
	go func() {
		err := server.ListenAndServe()
		if !errors.Is(err, http.ErrServerClosed) {
			log.Fatal(err)
		}
		log.Println("Stopped serving new connections")
	}()
	log.Println("Server started")
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
	<-sigChan
	ctx, shutdown := context.WithTimeout(context.Background(), time.Duration(serverConfig.ShutdownTimeout)*time.Second)
	defer shutdown()
	err = server.Shutdown(ctx)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Server gracefully stopped")
}
error message
❯ curl -v localhost:8080/
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
* Recv failure: Connection reset by peer
* Closing connection 0
curl: (56) Recv failure: Connection reset by peer
答案1
得分: 3
如评论中所提到的,问题与超时值太短有关 - 每个超时值为5纳秒。这是因为time.Duration被表示为两个瞬间之间经过的时间,以int64纳秒计数的形式。所以我需要将其转换为秒,以获得我期望的结果。
来自文档:
> "Duration表示两个瞬间之间经过的时间,以int64 纳秒 计数的形式。该表示将最大可表示的持续时间限制为大约290年。"
解决方案:
server := &http.Server{
	Addr:         fmt.Sprintf(":%s", serverConfig.Port),
	ReadTimeout:  time.Duration(serverConfig.ReadTimeout) * time.Second,
	WriteTimeout: time.Duration(serverConfig.WriteTimeout) * time.Second,
	IdleTimeout:  time.Duration(serverConfig.IdleTimeout) * time.Second,
	ErrorLog:     errorLog,
}
英文:
As mentioned in the comments, the issue is related to the timeout values being so short - 5 nanoseconds each. This is because time.Duration is represented as the elapsed time between two instants as an int64 nanosecond count. So I needed to convert that in to seconds, to get what I was expecting.
From the docs:
> "A Duration represents the elapsed time between two instants as an int64 nanosecond count. The representation limits the largest representable duration to approximately 290 years."
Solution:
server := &http.Server{
	Addr:         fmt.Sprintf(":%s", serverConfig.Port),
	ReadTimeout:  time.Duration(serverConfig.ReadTimeout) * time.Second,
	WriteTimeout: time.Duration(serverConfig.WriteTimeout) * time.Second,
	IdleTimeout:  time.Duration(serverConfig.IdleTimeout) * time.Second,
	ErrorLog:     errorLog,
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论