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.

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.

Appendix: 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)