unknwon/the-way-to-go_ZH_CN · error

"ERROR: " + info + " " + error.Error()

Error message

"ERROR: " + info + " " + error.Error()

What it means

Centralized checkError in the simple TCP server v1 (section 15.1): it takes an error plus an info context string and panics 'ERROR: <info> <err>' on any failure. The section's improvement notes state that all error checks were refactored into checkError, using the error context to trigger panic.

Source

Thrown at eBook/15.1.md:293

	checkError(err, "Write: wrote "+string(wrote)+" bytes.")
}

func handleMsg(length int, err error, msg []byte) {
	if length > 0 {
		print("<", length, ":")
		for i := 0; ; i++ {
			if msg[i] == 0 {
				break
			}
			fmt.Printf("%c", msg[i])
		}
		print(">")
	}
}

func checkError(error error, info string) {
	if error != nil {
		panic("ERROR: " + info + " " + error.Error()) // terminate program
	}
}
```
(**译者注:应该是由于 Go 版本的更新,会提示 os.EAGAIN undefined,修改后的代码:[simple_tcp_server_v1.go](examples/chapter_15/simple_tcp_server_v1.go)**)

都有哪些改进?

*	服务器地址和端口不再是硬编码,而是通过命令行参数传入,并通过 `flag` 包来读取这些参数。这里使用了 `flag.NArg()` 检查是否按照期望传入了 2 个参数:

```go
if flag.NArg() != 2 {
	panic("usage: host port")
}
```
传入的参数通过 `fmt.Sprintf()` 函数格式化成字符串
```go
hostAndPort := fmt.Sprintf("%s:%s", flag.Arg(0), flag.Arg(1))
```

View on GitHub (pinned to 7a54d34d36)

Solutions

  1. Validate the address early with net.SplitHostPort and strconv.Atoi(port) before ResolveTCPAddr
  2. Handle Accept errors with log-and-continue; treat only resolve/listen failures as fatal
  3. Move per-connection read/write errors into the handler: log, close conn, return
  4. Raise 'ulimit -n' or cap concurrent connections if EMFILE appears in the panic message

Example fix

// before
func checkError(error error, info string) {
    if error != nil {
        panic("ERROR: " + info + " " + error.Error())
    }
}

// after
func checkError(err error, info string) {
    if err != nil {
        log.Printf("%s: %v", info, err) // non-fatal: caller decides
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// validate the endpoint before ResolveTCPAddr
host, portStr, err := net.SplitHostPort(hostAndPort)
if err != nil {
    log.Fatal("bad address:", err)
}
if p, err := strconv.Atoi(portStr); err != nil || p < 1 || p > 65535 {
    log.Fatalf("bad port %q", portStr)
}

Try / catch

// keep the server alive when a handler panics
func connectionHandler(conn net.Conn) {
    defer func() {
        if r := recover(); r != nil {
            log.Println("handler panic:", r)
        }
    }()
    defer conn.Close()
    // ... reads/writes; treat EAGAIN as retry, EOF/reset as close
}

Prevention

When it happens

Trigger: net.ResolveTCPAddr failing on a malformed hostAndPort (bad host or non-numeric port); listener.Accept failing under fd exhaustion (EMFILE); connectionHandler's 25-byte-buffer read/write hitting a connection reset or looping on EAGAIN — whichever error first reaches checkError kills the server.

Common situations: Non-numeric port or malformed host passed as CLI arguments; too many open files under load (ulimit -n); clients dropping while the server writes its promo message; note the doc itself flags the os.EAGAIN undefined compile issue in the original listing.

Related errors


AI-assisted analysis of unknwon/the-way-to-go_ZH_CN@7a54d34d36 (2026-08-15). Data as JSON: /api/errors/114fdc4742e1a783. Report an issue: GitHub.