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
相关产品推荐
相关产品推荐

