libwebsockets 服务端获取过代理的真实连接IP

在使用 libwebsockets 开发服务端时,若客户端通过代理(如 Nginx、HAProxy 等)连接,直接获取的通常是代理服务器的 IP。要获取客户端真实 IP,需通过代理服务器传递的特定 HTTP 头信息(如 X-Forwarded-ForX-Real-IP)解析。以下是具体实现方法:

核心原理

代理服务器会在转发请求时,将客户端真实 IP 写入 HTTP 头字段(需代理提前配置),常见字段:

  • X-Forwarded-For:格式为 客户端真实IP, 代理1IP, 代理2IP(最左侧为真实IP)。
  • X-Real-IP:直接记录客户端真实IP(通常由一级代理设置)。

libwebsockets 服务端可在握手阶段解析这些头字段,提取真实 IP。

实现步骤

1. 代理服务器配置(以 Nginx 为例)

确保代理服务器正确添加转发头,示例 Nginx 配置:

nginx 复制代码
location /ws {
    proxy_pass http://127.0.0.1:8080;  # 指向libwebsockets服务端
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $host;
    # 关键:添加真实IP头
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
2. libwebsockets 服务端获取真实 IP

在 libwebsockets 回调函数中,通过 lws_hdr_copy 接口获取代理头字段,解析出真实 IP。

cpp 复制代码
#include <libwebsockets.h>
#include <string.h>
#include <stdio.h>

#define MAX_IP_LEN 64

// 解析X-Forwarded-For获取真实IP(取第一个IP)
static const char* get_real_ip_from_forwarded(const char* forwarded) {
    if (!forwarded) return NULL;
    static char real_ip[MAX_IP_LEN];
    // X-Forwarded-For格式:"client_ip, proxy1_ip, proxy2_ip"
    const char* comma = strchr(forwarded, ',');
    if (comma) {
        size_t len = comma - forwarded;
        if (len < MAX_IP_LEN) {
            memcpy(real_ip, forwarded, len);
            real_ip[len] = '\0';
            return real_ip;
        }
    }
    // 若没有逗号,整个字段即为真实IP
    return forwarded;
}

// 回调函数:处理WebSocket事件
static int callback_server(struct lws *wsi, enum lws_callback_reasons reason,
                           void *user, void *in, size_t len) {
    switch (reason) {
        case LWS_CALLBACK_ESTABLISHED: {
            // 连接建立时获取IP
            char client_ip[MAX_IP_LEN] = {0};
            char proxy_ip[MAX_IP_LEN] = {0};
            const char* real_ip = NULL;

            // 1. 获取代理头X-Forwarded-For
            if (lws_hdr_copy(wsi, proxy_ip, sizeof(proxy_ip), WSI_TOKEN_X_FORWARDED_FOR) > 0) {
                real_ip = get_real_ip_from_forwarded(proxy_ip);
            }
            // 2. 若X-Forwarded-For不存在,尝试X-Real-IP
            if (!real_ip && lws_hdr_copy(wsi, proxy_ip, sizeof(proxy_ip), WSI_TOKEN_X_REAL_IP) > 0) {
                real_ip = proxy_ip;
            }
            // 3. 若均不存在,使用默认的客户端IP(可能是代理IP)
            if (!real_ip) {
                lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), client_ip, sizeof(client_ip), NULL, 0);
                real_ip = client_ip;
            }

            printf("客户端真实IP: %s\n", real_ip);
            break;
        }
        // 其他事件处理(略)
        default:
            break;
    }
    return 0;
}

// 协议配置
static const struct lws_protocols protocols[] = {
    {
        "default",
        callback_server,
        0,  // 不使用用户数据
        4096, // 接收缓冲区大小
    },
    { NULL, NULL, 0, 0 } // 协议结束标记
};

int main() {
    struct lws_context_creation_info info;
    struct lws_context *context;

    memset(&info, 0, sizeof(info));
    info.port = 8080;
    info.protocols = protocols;
    info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;

    // 创建WebSocket上下文
    context = lws_create_context(&info);
    if (!context) {
        fprintf(stderr, "创建上下文失败\n");
        return 1;
    }

    printf("服务端启动,端口: %d\n", info.port);
    // 事件循环
    while (lws_service(context, 50) >= 0);

    // 清理资源
    lws_context_destroy(context);
    return 0;
}

关键代码解析

  1. 获取代理头字段

    通过 lws_hdr_copy 函数提取 X-Forwarded-ForX-Real-IP 头,对应的令牌分别为:

    • WSI_TOKEN_X_FORWARDED_FOR:对应 X-Forwarded-For
    • WSI_TOKEN_X_REAL_IP:对应 X-Real-IP
  2. 解析 X-Forwarded-For

    该字段可能包含多个 IP(逗号分隔),取第一个即为客户端真实 IP(如 client_ip, proxy1, proxy2 中提取 client_ip)。

  3. 降级策略

    若代理头不存在,通过 lws_get_peer_addresses 获取原始连接 IP(通常是代理服务器 IP)。

注意事项

  1. 代理信任问题

    仅信任已知代理服务器的 X-Forwarded-ForX-Real-IP 头,防止客户端伪造这些头字段。可在服务端限制仅接收特定代理 IP 的请求。

  2. libwebsockets 版本兼容

    不同版本的 libwebsockets 头字段令牌可能不同(如旧版本可能需要直接使用字符串 X-Forwarded-For 而非枚举值),需根据实际版本调整。

  3. IPv6 支持

    若需支持 IPv6,需调整 IP 缓冲区大小(MAX_IP_LEN),并处理 IPv6 格式(如 ::1)。

通过以上方法,libwebsockets 服务端可正确获取经过代理的客户端真实 IP,适用于反向代理、负载均衡等场景。

相关推荐
予昊4 小时前
从零实现“在线五子棋对战“:WebSocket 实时通信 + 段位匹配
java·开发语言·网络·websocket
隔窗听雨眠13 小时前
大模型实时通信协议深度对比:SSE、WebSocket与gRPC的选型指南与实战解析
网络·websocket·网络协议
艾莉丝努力练剑3 天前
【AI大模型接入SDK】WebSocket & SSE 协议
网络·c++·websocket·网络协议·学习·面试
虎王物联4 天前
PHP Swoole实现物联网MQTT数据订阅与WebSocket实时推送
物联网·mqtt·websocket·php·swoole
一条泥憨鱼6 天前
【从0开始学习计算机网络】| WebSocket-握手、全双工和心跳
websocket·计算机网络·网络安全·https·dns
GitLqr6 天前
从 TCP 底层看实时通信:为什么 SSE 往往比 WebSocket 更香?
websocket·http·openai
脉动数据行情17 天前
Python WebSocket 国内商品期货|螺纹钢 & 铁矿石实时行情监听
开发语言·python·websocket
宠友信息7 天前
内容社区源码开发实践解析,用Spring Boot打造1:1仿小红书源码平台
java·spring boot·redis·websocket·mysql·spring·uni-app
limingade9 天前
智能拨号器APP-外部WebSocket话单推送系统接口文档
网络·websocket·智能拨号器app·远程推送号码到手机拨打电话·手机app稳定长连接·手机拨打电话后推送话单和录音