leuzz 2024-06-27 18:07 采纳率: 100%
浏览 7
已结题

udp socket,bind 0.0.0.0 ,如何自动选取用户访问的服务器IP来回复数据

有个问题请教一下, 有台服务器有一张网卡, 网卡上有 3个公网IP, 我创建了一个udp socket,bind 0.0.0.0 的 7000端口, 用户1 来访问IP1 的7000端口, 服务程序会用默认的IP1 给用户1回复数据, 但是用户2来访问IP2的7000端口, 服务器仍然用 IP1来给用户2 回复数据, 有办法让服务器用IP2给用户2回复吗 ? 用iptables 命令可以解决吗? 我的意思是用户有很多个, 用户的IP也不确定, 用户会用随机的公网IP 来随机访问我服务器的iP1 Ip2 i-p3 I-P4 等等 , 我希望用户访问我的哪个I P, 我的程序就用哪个i p给用户回复数据

  • 写回答

16条回答 默认 最新

  • 杨同学* 2024-06-28 09:11
    关注

    该回答结合ChatGPT4o及杨同学*共同作答, 如有帮助,还请采纳。
    要解决这个问题,您可以使用较低级的套接字接口来获取接收到的数据的目的地址。通过这种方式,您可以获取用户访问的具体服务器IP地址,然后用相同的IP地址进行回复。具体地,可以使用 recvmsgsendmsg 系列函数,它们提供了更加灵活的控制,包括访问到达数据包的目的地址。

    以下是一个示例代码,展示了如何实现这一功能:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <net/if.h>
    
    #define PORT 7000
    #define BUF_SIZE 1024
    
    int main() {
        int sockfd;
        struct sockaddr_in server_addr, client_addr;
        char buffer[BUF_SIZE];
        socklen_t addr_len = sizeof(client_addr);
    
        // 创建套接字
        if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
            perror("socket creation failed");
            exit(EXIT_FAILURE);
        }
    
        // 绑定套接字到所有可用地址
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = INADDR_ANY;
        server_addr.sin_port = htons(PORT);
    
        if (bind(sockfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
            perror("bind failed");
            close(sockfd);
            exit(EXIT_FAILURE);
        }
    
        while (1) {
            struct iovec iov[1];
            struct msghdr msg;
            struct cmsghdr *cmsg;
            char cbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];
            struct in_pktinfo *pktinfo;
    
            memset(&msg, 0, sizeof(msg));
            iov[0].iov_base = buffer;
            iov[0].iov_len = sizeof(buffer);
            msg.msg_name = &client_addr;
            msg.msg_namelen = sizeof(client_addr);
            msg.msg_iov = iov;
            msg.msg_iovlen = 1;
            msg.msg_control = cbuf;
            msg.msg_controllen = sizeof(cbuf);
    
            ssize_t len = recvmsg(sockfd, &msg, 0);
            if (len < 0) {
                perror("recvmsg failed");
                continue;
            }
    
            buffer[len] = '\0';
            printf("Received message: %s\n", buffer);
    
            for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
                if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
                    pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
                    char ip[INET_ADDRSTRLEN];
                    inet_ntop(AF_INET, &pktinfo->ipi_addr, ip, sizeof(ip));
                    printf("Message received on IP address: %s\n", ip);
    
                    // 使用接收到的数据的目的地址作为源地址发送回复
                    struct sockaddr_in reply_addr;
                    memset(&reply_addr, 0, sizeof(reply_addr));
                    reply_addr.sin_family = AF_INET;
                    reply_addr.sin_addr = pktinfo->ipi_addr;
                    reply_addr.sin_port = htons(0); // 让系统选择端口
    
                    int reply_sock = socket(AF_INET, SOCK_DGRAM, 0);
                    if (reply_sock < 0) {
                        perror("reply socket creation failed");
                        continue;
                    }
    
                    if (bind(reply_sock, (struct sockaddr *)&reply_addr, sizeof(reply_addr)) < 0) {
                        perror("bind failed");
                        close(reply_sock);
                        continue;
                    }
    
                    sendto(reply_sock, buffer, len, 0, (struct sockaddr *)&client_addr, addr_len);
                    close(reply_sock);
                    break;
                }
            }
        }
    
        close(sockfd);
        return 0;
    }
    

    解释

    1. 创建和绑定套接字:创建UDP套接字并绑定到所有可用地址(0.0.0.0)。
    2. 接收数据:使用 recvmsg 接收数据包, msg 结构中设置控制消息缓冲区。
    3. 提取目的地址:从控制消息中提取目的地址(即,客户端访问的服务器IP)。
    4. 发送回复:创建新的套接字,绑定到提取的目的地址,使用 sendto 发送回复给客户端。

    这样,服务器将根据客户端访问的具体IP地址来发送回复。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(15条)

报告相同问题?

问题事件

  • 系统已结题 7月6日
  • 已采纳回答 6月28日
  • 修改了问题 6月27日
  • 创建了问题 6月27日

悬赏问题

  • ¥15 35114 SVAC视频验签的问题
  • ¥15 impedancepy
  • ¥15 在虚拟机环境下完成以下,要求截图!
  • ¥15 求往届大挑得奖作品(ppt…)
  • ¥15 如何在vue.config.js中读取到public文件夹下window.APP_CONFIG.API_BASE_URL的值
  • ¥50 浦育平台scratch图形化编程
  • ¥20 求这个的原理图 只要原理图
  • ¥15 vue2项目中,如何配置环境,可以在打完包之后修改请求的服务器地址
  • ¥20 微信的店铺小程序如何修改背景图
  • ¥15 UE5.1局部变量对蓝图不可见