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
- Validate the address early with net.SplitHostPort and strconv.Atoi(port) before ResolveTCPAddr
- Handle Accept errors with log-and-continue; treat only resolve/listen failures as fatal
- Move per-connection read/write errors into the handler: log, close conn, return
- 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
- Validate host:port with net.SplitHostPort + numeric port check before binding
- Separate fatal startup errors from per-connection errors; only the former may terminate
- Treat EAGAIN as a retry signal, not a termination condition
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.