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

NATS客户端能否通过公共代理服务器连接NATS服务器?求解决方案

可行解决方案

针对NATS客户端通过公共代理建立连接的需求,以下是几种可靠实现方式:

方式一:利用SOCKS5代理(推荐)

NATS基于TCP协议传输,SOCKS5代理天然支持TCP流量转发,libnats库可通过两种方式适配:

1. 环境变量快速配置

若公共代理支持SOCKS5,启动客户端前直接设置环境变量即可,无需修改代码:

# 无认证的SOCKS5代理
export ALL_PROXY=socks5://proxy-host:proxy-port

# 需要账号密码的SOCKS5代理
export ALL_PROXY=socks5://username:password@proxy-host:proxy-port

启动客户端后,libnats会自动读取环境变量,通过代理建立与NATS服务器的连接。

2. 代码自定义连接器

若需在代码中硬编码代理逻辑,使用natsOptions_SetCustomConnector实现自定义连接:

#include <iostream>
#include <nats/nats.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>

// 自定义SOCKS5代理连接函数
natsStatus customConnector(int *pSock, const char *targetHost, uint16_t targetPort, void *closure)
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0)
        return NATS_IO_ERROR;

    // 连接到SOCKS5代理服务器
    struct sockaddr_in proxyAddr;
    memset(&proxyAddr, 0, sizeof(proxyAddr));
    proxyAddr.sin_family = AF_INET;
    proxyAddr.sin_port = htons(1080); // 代理端口
    inet_pton(AF_INET, "proxy-host", &proxyAddr.sin_addr);

    if (connect(sock, (struct sockaddr*)&proxyAddr, sizeof(proxyAddr)) < 0)
    {
        close(sock);
        return NATS_CONNECT_ERROR;
    }

    // 此处需补充SOCKS5协议握手逻辑,完成后socket即可用于NATS通信
    // 握手逻辑需按照SOCKS5标准实现,目标地址为targetHost和targetPort

    *pSock = sock;
    return NATS_OK;
}

int main(int argc, char **argv)
{
    natsConnection      *conn = NULL;
    natsSubscription    *sub  = NULL;
    natsMsg             *msg  = NULL;
    natsOptions         *opts = NULL;
    natsStatus          s;

    printf("Listening on subject 'foo'\n");

    s = natsOptions_Create(&opts);
    // 设置自定义连接器
    s = natsOptions_SetCustomConnector(opts, customConnector, NULL);
    // 设置NATS服务器地址
    s = natsOptions_SetURL(opts, "nats://nats-server-host:4222");
    if (s != NATS_OK) {
        printf("set config error\n");
        return -1;
    }

    s = natsConnection_Connect(&conn, opts);
    if (s != NATS_OK) {
        printf("connect error\n");
        return -1;
    }
    
    s = natsConnection_SubscribeSync(&sub, conn, "foo");
    if (s != NATS_OK) {
        printf("subscribe error\n");
        return -1;
    }

    while (true) {
        s = natsSubscription_NextMsg(&msg, sub, 5000);
        if (s == NATS_OK)
        {
            printf("Received msg: %s - %.*s\n",
                   natsMsg_GetSubject(msg),
                   natsMsg_GetDataLength(msg),
                   natsMsg_GetData(msg));

            natsMsg_Destroy(msg);
        }
    }

    natsSubscription_Destroy(sub);
    natsConnection_Destroy(conn);

    if (s != NATS_OK)
    {
        nats_PrintLastErrorStack(stderr);
        exit(2);
    }

    return 0;
}

方式二:Nginx中转适配HTTP公共代理

若公共代理为HTTP类型(仅支持CONNECT方法),可在本地部署Nginx作为中转,让Nginx先通过HTTP代理连接NATS服务器,再让客户端连接本地Nginx:

Nginx配置示例

stream {
    server {
        listen 127.0.0.1:4223;
        proxy_pass nats-server-host:4222;
        proxy_connect_timeout 10s;
        # 启用HTTP代理CONNECT支持
        proxy_http_connect on;
        # 配置公共HTTP代理地址
        proxy_http_connect_address public-proxy-host:proxy-port;
        # 若HTTP代理需要认证
        proxy_http_connect_user "proxy-username";
        proxy_http_connect_password "proxy-password";
    }
}

客户端代码修改

将NATS服务器URL改为本地Nginx的监听地址:

s = natsOptions_SetURL(opts, "nats://127.0.0.1:4223");

方式三:手动建立代理连接后传入NATS

先手动完成与公共代理的握手(以HTTP CONNECT为例),拿到可用socket后通过natsOptions_SetSocket传给NATS客户端:

#include <iostream>
#include <nats/nats.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char **argv)
{
    natsConnection      *conn = NULL;
    natsSubscription    *sub  = NULL;
    natsMsg             *msg  = NULL;
    natsOptions         *opts = NULL;
    natsStatus          s;
    int sock = -1;

    printf("Listening on subject 'foo'\n");

    // 1. 手动连接HTTP公共代理并完成CONNECT握手
    sock = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in proxyAddr;
    memset(&proxyAddr, 0, sizeof(proxyAddr));
    proxyAddr.sin_family = AF_INET;
    proxyAddr.sin_port = htons(8080); // HTTP代理端口
    inet_pton(AF_INET, "public-proxy-host", &proxyAddr.sin_addr);
    
    if (connect(sock, (struct sockaddr*)&proxyAddr, sizeof(proxyAddr)) < 0)
    {
        perror("proxy connect failed");
        return -1;
    }

    // 发送CONNECT请求到代理,目标为NATS服务器
    char req[256];
    snprintf(req, sizeof(req), "CONNECT nats-server-host:4222 HTTP/1.1\r\nHost: nats-server-host:4222\r\n\r\n");
    send(sock, req, strlen(req), 0);

    // 读取代理响应,确认连接成功
    char resp[256];
    recv(sock, resp, sizeof(resp), 0);
    if (strstr(resp, "200 Connection established") == NULL)
    {
        perror("proxy connect failed");
        close(sock);
        return -1;
    }

    // 2. 配置NATS使用已建立的socket
    s = natsOptions_Create(&opts);
    s = natsOptions_SetSocket(opts, sock);
    // 仍需设置URL,但NATS会复用传入的socket
    s = natsOptions_SetURL(opts, "nats://nats-server-host:4222");
    if (s != NATS_OK) {
        printf("set config error\n");
        close(sock);
        return -1;
    }

    s = natsConnection_Connect(&conn, opts);
    if (s != NATS_OK) {
        printf("connect error\n");
        close(sock);
        return -1;
    }
    
    s = natsConnection_SubscribeSync(&sub, conn, "foo");
    if (s != NATS_OK) {
        printf("subscribe error\n");
        natsConnection_Destroy(conn);
        close(sock);
        return -1;
    }

    while (true) {
        s = natsSubscription_NextMsg(&msg, sub, 5000);
        if (s == NATS_OK)
        {
            printf("Received msg: %s - %.*s\n",
                   natsMsg_GetSubject(msg),
                   natsMsg_GetDataLength(msg),
                   natsMsg_GetData(msg));

            natsMsg_Destroy(msg);
        }
    }

    natsSubscription_Destroy(sub);
    natsConnection_Destroy(conn);
    close(sock);

    if (s != NATS_OK)
    {
        nats_PrintLastErrorStack(stderr);
        exit(2);
    }

    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 03:44:57