在Go服务器中,”Connection reset by peer”错误是由对等方重置连接引起的。

huangapple go评论141阅读模式
英文:

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 &#39;cmd|server.go|go.mod|go.sum|server.yaml&#39;
├── cmd
│&#160;&#160; └── server.go
├── go.mod
├── go.sum
└── server.yaml

server.go

package main

import (
	&quot;context&quot;
	&quot;errors&quot;
	&quot;fmt&quot;
	&quot;log&quot;
	&quot;net/http&quot;
	&quot;os&quot;
	&quot;os/signal&quot;
	&quot;syscall&quot;
	&quot;time&quot;

	&quot;gopkg.in/yaml.v3&quot;
)

type Config struct {
	Port            string `yaml:&quot;Port&quot;`
	ReadTimeout     int    `yaml:&quot;ReadTimeout&quot;`
	WriteTimeout    int    `yaml:&quot;WriteTimeout&quot;`
	IdleTimeout     int    `yaml:&quot;IdleTimeout&quot;`
	ShutdownTimeout int    `yaml:&quot;ShutdownTimeout&quot;`
	ErrorLog        string `yaml:&quot;ErrorLog&quot;`
}

func main() {
	if len(os.Args) != 2 {
		log.Fatal(&quot;Missing arguments&quot;)
	}

	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(&amp;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, &quot;ERROR : &quot;, log.LstdFlags|log.Lshortfile)

	server := &amp;http.Server{
		Addr:         fmt.Sprintf(&quot;:%s&quot;, serverConfig.Port),
		ReadTimeout:  time.Duration(serverConfig.ReadTimeout),
		WriteTimeout: time.Duration(serverConfig.WriteTimeout),
		IdleTimeout:  time.Duration(serverConfig.IdleTimeout),
		ErrorLog:     errorLog,
	}

	http.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, &quot;Hello from Go!&quot;)
	})

	log.Println(&quot;Starting the server...&quot;)
	go func() {
		err := server.ListenAndServe()
		if !errors.Is(err, http.ErrServerClosed) {
			log.Fatal(err)
		}
		log.Println(&quot;Stopped serving new connections&quot;)
	}()
	log.Println(&quot;Server started&quot;)

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
	&lt;-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(&quot;Server gracefully stopped&quot;)
}

error message

❯ curl -v localhost:8080/
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
&gt; GET / HTTP/1.1
&gt; Host: localhost:8080
&gt; User-Agent: curl/7.64.1
&gt; Accept: */*
&gt;
* 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 := &amp;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."

Reference

Solution:

server := &amp;http.Server{
	Addr:         fmt.Sprintf(&quot;:%s&quot;, 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,
}

huangapple
  • 本文由 发表于 2022年1月30日 05:57:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/70910073.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定