You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言TCP连接PostgreSQL遇SSL请求后接收未加密数据错误

问题分析与解决

问题根源

你直接通过TCP连接PostgreSQL服务器后就发送查询语句,但服务器要求使用SSL加密连接,而你的代码完全跳过了SSL握手流程,直接发送明文查询,这就导致服务器触发FATAL错误,返回的"N"是PostgreSQL在SSL协商阶段的响应(表示服务器期望SSL握手,但收到了非法数据)。

服务器日志翻译如下:

2023-02-22 22:59:49.834 PKT [54802] FATAL: SSL请求后收到未加密数据
2023-02-22 22:59:49.834 PKT [54802] DETAIL: 这可能是客户端软件bug,也可能是中间人攻击的迹象。

解决方法

方法1:使用PostgreSQL官方驱动(推荐)

不要手动实现TCP连接和协议,直接用成熟的Go驱动,它们会自动处理SSL握手、认证、报文解析等所有细节。比如github.com/lib/pq或者github.com/jackc/pgx:

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/lib/pq"
)

func main() {
    // 连接字符串根据你的数据库配置调整,sslmode设为服务器要求的模式(比如require、verify-full)
    connStr := "user=你的用户名 dbname=你的数据库名 sslmode=require host=localhost port=5432"
    db, err := sql.Open("postgres", connStr)
    if err != nil {
        fmt.Printf("连接失败:%v\n", err)
        return
    }
    defer db.Close()

    // 测试查询
    rows, err := db.Query("SELECT 1")
    if err != nil {
        fmt.Printf("查询失败:%v\n", err)
        return
    }
    defer rows.Close()

    var num int
    for rows.Next() {
        err := rows.Scan(&num)
        if err != nil {
            fmt.Printf("结果解析失败:%v\n", err)
            return
        }
        fmt.Printf("查询结果:%d\n", num)
    }
}

方法2:手动实现SSL握手(仅用于学习,不推荐)

如果一定要手动处理TCP连接,必须先完成SSL握手:

  1. 连接建立后,先发送PostgreSQL的SSL请求标识报文
  2. 确认服务器同意SSL后,建立TLS连接
  3. 再通过TLS连接发送查询

示例代码:

package main

import (
    "crypto/tls"
    "fmt"
    "net"
)

func main() {
    // 建立TCP连接
    conn, err := net.Dial("tcp", "localhost:5432")
    if err != nil {
        fmt.Printf("TCP连接失败:%v\n", err)
        return
    }
    defer conn.Close()

    // 发送PostgreSQL SSL请求报文
    sslReq := []byte{0x00, 0x00, 0x00, 0x08, 0x04, 0xd2, 0x16, 0x2f}
    _, err = conn.Write(sslReq)
    if err != nil {
        fmt.Printf("发送SSL请求失败:%v\n", err)
        return
    }

    // 读取服务器响应
    resp := make([]byte, 1)
    _, err = conn.Read(resp)
    if err != nil {
        fmt.Printf("读取SSL响应失败:%v\n", err)
        return
    }

    if string(resp) != "S" {
        fmt.Printf("服务器不支持SSL,响应:%s\n", resp)
        return
    }

    // 建立TLS连接
    tlsConn := tls.Client(conn, &tls.Config{
        ServerName: "localhost", // 替换为你的服务器主机名
    })
    err = tlsConn.Handshake()
    if err != nil {
        fmt.Printf("TLS握手失败:%v\n", err)
        return
    }
    defer tlsConn.Close()

    // 发送查询语句
    query := "SELECT 1;"
    _, err = tlsConn.Write([]byte(query))
    if err != nil {
        fmt.Printf("发送查询失败:%v\n", err)
        return
    }

    // 读取响应
    buf := make([]byte, 50000)
    n, err := tlsConn.Read(buf)
    if err != nil {
        fmt.Printf("读取响应失败:%v\n", err)
        return
    }
    fmt.Printf("收到响应:%s\n", string(buf[:n]))
}

注意事项

手动实现PostgreSQL协议需要处理大量细节(比如报文的长度标识、认证流程、数据解析等),很容易出错,实际项目中强烈推荐使用官方驱动。

内容的提问来源于stack exchange,提问作者Kamlesh Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 19:09:30