🏆本文收录于《CSDN问答解答》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
问题描述
有个问题请教一下, 有台服务器有一张网卡, 网卡上有 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给用户回复数据
如上问题有来自我自身项目开发,有的收集网站,有的来自读者,如有侵权,立马删除。
解决方案
如下是上述问题的解决方案,仅供参考:
在 UDP 通信中,当服务器端的 socket 绑定到 0.0.0.0
时,它能够接收来自任何网络接口上的指定端口的数据。但是,当发送响应时,如果没有特定的机制来决定使用哪个源地址,操作系统通常会使用一个默认的源地址。
要实现你的需求,即让服务器使用与客户端请求相同的源地址回复数据,有几种可能的方法:
使用多个 socket:
为每个公网 IP 创建一个单独的 socket,并分别绑定到对应的 IP 地址和端口。这样,每个 socket 只会接收发送到其绑定 IP 的数据。然后,使用该 socket 发送响应。使用
recvmsg
系统调用:
在 Linux 上,可以使用recvmsg
系统调用来接收数据,它会提供发送方的地址信息,包括 IP 地址。根据这个信息,你可以决定使用哪个 IP 地址来回复。使用
iproute2
工具:
使用iproute2
工具设置路由规则,使得不同的源地址的数据包通过不同的网络接口发送。使用
iptables
:
iptables
可以用来设置规则,将进入特定端口的流量转发到不同的本地 IP。但是,这种方法可能比较复杂,并且可能需要对每个 IP 地址设置单独的规则。使用
setsockopt
设置IP_PKTINFO
:
使用setsockopt
来设置IP_PKTINFO
选项,这样接收数据时可以获取原始的目的地信息,并使用这个信息来决定回复的源地址。
下面是一个使用 recvmsg
和 IP_PKTINFO
的简单示例:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> // for IP_PKTINFO
int main() {
int s;
struct sockaddr_in server_addr;
char buf[1024];
struct iovec iov;
struct msghdr msg;
char cmbuf[1024];
char *pdata = buf; // 数据存放点
struct cmsghdr *cmsg;
struct in_pktinfo *pi;
// 创建 socket
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
// 绑定端口
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(7000);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
// 设置IP_PKTINFO选项
int on = 1;
if (setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)) < 0) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
memset(&msg, 0, sizeof(msg));
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmbuf;
msg.msg_controllen = sizeof(cmbuf);
while (1) {
// 接收数据
if (recvmsg(s, &msg, 0) < 0) {
perror("recvmsg");
continue;
}
// 解析控制 message,获取原始目的地信息
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
pi = (struct in_pktinfo *)CMSG_DATA(cmsg);
char addr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(pi->ipi_addr), addr, sizeof(addr));
printf("Received data from: %s\n", addr);
// 使用 pi->ipi_addr 作为源地址回复数据
// ...
break;
}
}
}
close(s);
return 0;
}
请注意,这个示例代码是一个基础的演示,它没有实现完整的回复逻辑。在实际应用中,你可能需要根据获取到的源地址信息来选择正确的网络接口进行回复。此外,由于网络环境和操作系统的差异,你可能需要调整代码以适应你的特定需求。
希望如上措施及解决方案能够帮到有需要的你。
PS:如若遇到采纳如下方案还是未解决的同学,希望不要抱怨&&急躁,毕竟影响因素众多,我写出来也是希望能够尽最大努力帮助到同类似问题的小伙伴,即把你未解决或者产生新Bug黏贴在评论区,我们大家一起来努力,一起帮你看看,可以不咯。
若有对当前Bug有与如下提供的方法不一致,有个不情之请,希望你能把你的新思路或新方法分享到评论区,一起学习,目的就是帮助更多所需要的同学,正所谓「赠人玫瑰,手留余香」。
☀️写在最后
ok,以上就是我这期的Bug修复内容啦,如果还想查找更多解决方案,你可以看看我专门收集Bug及提供解决方案的专栏《CSDN问答解惑-专业版》,都是实战中碰到的Bug,希望对你有所帮助。到此,咱们下期拜拜。
码字不易,如果这篇文章对你有所帮助,帮忙给 bug菌 来个一键三连(关注、点赞、收藏) ,您的支持就是我坚持写作分享知识点传播技术的最大动力。
同时也推荐大家关注我的硬核公众号:「猿圈奇妙屋」 ;以第一手学习bug菌的首发干货,不仅能学习更多技术硬货,还可白嫖最新BAT大厂面试真题、4000G Pdf技术书籍、万份简历/PPT模板、技术文章Markdown文档等海量资料,你想要的我都有!
📣关于我
我是bug菌,CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云2023年度十佳博主,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿哇。