最直接的网络连通性测试是用 net.dial 建立 tcp 连接,需显式设置 context.withtimeout 防卡死,地址格式为 "host:port",返回 nil 仅表示三次握手成功;http 测试应使用带超时的 http.client 并检查 statuscode;自定义协议需手动读写并设读写超时;并发压测须自定义 transport 避免连接池瓶颈。

用 net.Dial 快速建立 TCP 连接并验证通断
最直接的网络连通性测试,就是尝试建立一个 TCP 连接。Go 的 net.Dial 是首选,它不发送应用层数据,只完成三次握手,适合判断端口是否可达。
常见错误是忽略超时控制——默认阻塞直到系统级超时(可能长达数分钟),导致测试卡死。必须显式设置 context.WithTimeout。
- 使用
net.DialTimeout(已弃用但仍可用)或更推荐的net.DialContext+context.WithTimeout - 地址格式必须是
"host:port",例如"google.com:80";IP 地址需带端口,不能只写"127.0.0.1" - 返回
nil错误仅表示连接成功,不代表服务可交互;需进一步读写才能确认协议层可用性
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
conn, err := net.DialContext(ctx, "tcp", "localhost:8080")
if err != nil {
log.Printf("connect failed: %v", err) // 如 "dial tcp [::1]:8080: connect: connection refused"
return
}
defer conn.Close()
log.Println("TCP connect succeeded")
用 http.Get 测试 HTTP 服务可用性与响应内容
如果目标是 Web 服务,http.Get 比手写 TCP 更贴近真实使用场景,它自动处理 DNS、TLS、重定向和状态码解析。
容易被忽略的是:默认客户端没有超时,且对 4xx/5xx 响应不会报错(err == nil),仅靠 resp.StatusCode 判断是否“成功”。
立即学习“go语言免费学习笔记(深入)”;
- 务必自定义
http.Client并设置Timeout,避免 hang 住 - 检查
resp.StatusCode范围,如只接受2xx,需手动判断并返回错误 - 调用
resp.Body.Close()否则会泄漏底层连接(影响复用和资源) - 若需携带 Header 或 POST 数据,改用
http.NewRequestWithContext+client.Do
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Get("https://httpbin.org/get")
if err != nil {
log.Printf("HTTP request failed: %v", err)
return
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
log.Printf("unexpected status code: %d", resp.StatusCode)
return
}
log.Println("HTTP endpoint is up and returned 2xx")
用 bufio.Scanner 和 conn.Write 测试自定义协议交互
当服务不是 HTTP,而是私有 TCP 协议(比如 Redis、MQTT 简化版、或内部二进制协议),就得手动收发数据。此时重点不是连上,而是能否正确交换消息。
常见坑是缓冲区管理不当:Read 可能只读到部分响应,Write 不保证一次发完,而 bufio.Scanner 默认按行切割,对无换行协议会卡死。
- 对简单文本协议,用
bufio.NewReader(conn).ReadString('\n')比Scanner更可控 - 发送后务必调用
conn.SetReadDeadline防止无限等待响应 - 二进制协议建议用
binary.Write/binary.Read处理结构体,避免字节序和对齐问题 - 注意关闭连接前先
conn.Flush()(若用了bufio.Writer)
conn, _ := net.Dial("tcp", "localhost:9000")
defer conn.Close()
<p>// 发送字符串命令(假设协议以 \n 结尾)
conn.Write([]byte("PING\n"))</p><p>// 设置读取超时
conn.SetReadDeadline(time.Now().Add(2 * time.Second))
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
log.Printf("read response failed: %v", err)
return
}
log.Printf("got response: %s", buf[:n])
并发压测时要注意 http.DefaultClient 的连接复用与限制
做多 goroutine 并发请求时,直接用 http.Get 看似简单,但背后共享的 http.DefaultClient 使用全局 http.DefaultTransport,其默认配置会成为瓶颈。
典型表现是并发量一上去就大量超时或连接拒绝,日志里出现 "dial tcp xxx: i/o timeout" 或 "too many open files",其实是连接池或文件描述符耗尽。
- 自定义
http.Transport,调大MaxIdleConns、MaxIdleConnsPerHost和IdleConnTimeout - 不要在循环里反复创建
http.Client,复用单个实例(它是 safe for concurrent use) - 若压测目标是单台机器,注意系统
ulimit -n限制,必要时调整 - 连接复用依赖正确的
Connection: keep-alive响应头,服务端不支持会导致频繁重建连接
真正难调试的,往往不是代码逻辑,而是 transport 层的隐式行为——比如默认只允许每 host 最多 2 个空闲连接,却没人改它。










