我有一个服务器有几个不同的IP地址。此时,每个IP都可以接收一个UDP请求,但是响应的总是相同的IP,请求者不喜欢这样做。
长话短说,这是所有必要的代码:
int sock;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
memset((char*)&serv_addr, 0, sizeof(serv_addr));
sock = socket(AF_INET, SOCK_DGRAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PORT);
...
recvfrom(sock, buffer, BUFLEN, 0, (struct sockaddr *)&cli_addr, &clilen);
...
sendto(sock, resData, resLen, 0, (struct sockaddr *)&cli_addr, sizeof(cli_addr));我希望能够以某种方式指定使用哪个IP来发送我的数据包(对于每个请求,这可能有所不同),但是我不知道如何使用套接字来完成这一任务。我在这个领域并不是很有经验,所以我能得到的帮助都是非常感谢的。
编辑在下面
我在这里接受的答案中找到了一个潜在的解决方案:如何在Linux中重新绑定udp套接字
然而,一个新的问题出现了。我如何知道哪个IP/接口收到了请求?这样我就可以使用IP/接口进行响应了。
发布于 2013-10-15 11:18:17
我已经解决了我的问题,并以良好的方式,解决办法必须张贴!我没有使用SOCK_RAW,也没有将接口绑定到本地IP或诸如此类。这是一个混合100页的谷歌和一些堆叠溢出,所以我有点难过,因为我没有保存链接,以提供正确的学分。
在代码中可能有明显的缺陷,因为我不是专家,但是这是我想出的解决方案,而且效果很好。我刚刚开始清理代码(当您尝试从100个不同的页面中获取内容并将它们组合在一起时,代码会变得很混乱)。总之,这里是:
接收部分:
int sock;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
char buffer[BUFLEN];
// Next
memset((char*)&serv_addr, 0, sizeof(serv_addr));
sock = socket(AF_INET, SOCK_DGRAM, 0);
serv_addr.sin_family = AF_INET;
//serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PORT);
clilen = sizeof(cli_addr);
if (bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
error("ERROR on binding");
}
bool opt = true;
setsockopt(sock, IPPROTO_IP, IP_PKTINFO, (char *)&opt, sizeof(opt));
// Packet data will be saved in buffer
bzero(buffer, BUFLEN);
struct iovec vector;
vector.iov_base = buffer;
vector.iov_len = sizeof(buffer);
struct msghdr msg;
msg.msg_name = &cli_addr;
msg.msg_namelen = sizeof(cli_addr);
msg.msg_iov = &vector;
msg.msg_iovlen = 1;
int flags = 0;
// Not sure what controlBuffer contains at this point
char controlBuffer[1024];
msg.msg_control = controlBuffer;
msg.msg_controllen = 1024;
// Recv packet
int bytes = ::recvmsg(sock, &msg, flags);
struct cmsghdr *cmsg;
struct in_pktinfo *dest_ip_ptr;
int dest_ip = 0;
// Loop through IP header messages
cmsg = CMSG_FIRSTHDR(&msg);
for ( cmsg = CMSG_FIRSTHDR(&msg);
cmsg != NULL;
cmsg = CMSG_NXTHDR( &msg, cmsg ) )
{
if (cmsg->cmsg_level != IPPROTO_IP ||
cmsg->cmsg_type != IP_PKTINFO)
{
continue;
}
// Get IP (int)
struct in_pktinfo *dest_ip_ptr = (struct in_pktinfo*)CMSG_DATA(cmsg);
dest_ip = dest_ip_ptr->ipi_addr.s_addr;
}
// Format IP
unsigned char ipParts[4];
ipParts[0] = dest_ip & 0xFF;
ipParts[1] = (dest_ip >> 8) & 0xFF;
ipParts[2] = (dest_ip >> 16) & 0xFF;
ipParts[3] = (dest_ip >> 24) & 0xFF;发送部分:
// Build source sockaddr
struct sockaddr_in src_addr;
memset(&src_addr, 0, sizeof(struct sockaddr_in));
src_addr.sin_family = AF_INET;
// Save IP into a char array
char destIp[16];
memset(destIp, 0, sizeof(destIp));
sprintf(destIp, "%d.%d.%d.%d", ipParts[0], ipParts[1], ipParts[2], ipParts[3]);
inet_aton(destIp, &(src_addr.sin_addr));
char cmbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];
bzero(buffer, BUFLEN);
int len = dp.getRaw(buffer);
struct msghdr mh;
memset(&mh, 0, sizeof(mh));
struct cmsghdr *cmsg_send;
struct in_pktinfo *pktinfo;
struct iovec iov[1];
iov[0].iov_base = buffer;
iov[0].iov_len = BUFLEN;
mh.msg_name = &cli_addr; // destination address of packet
mh.msg_namelen = sizeof(cli_addr);
mh.msg_control = cmbuf;
mh.msg_controllen = sizeof(cmbuf);
mh.msg_flags = 0;
mh.msg_iov = iov;
mh.msg_iovlen = 1;
// after initializing msghdr & control data to CMSG_SPACE(sizeof(struct in_pktinfo))
cmsg_send = CMSG_FIRSTHDR(&mh);
cmsg_send->cmsg_level = IPPROTO_IP;
cmsg_send->cmsg_type = IP_PKTINFO;
cmsg_send->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg_send);
pktinfo->ipi_ifindex = 0;
pktinfo->ipi_spec_dst = src_addr.sin_addr;
int rc = sendmsg(sock, &mh, 0);发布于 2013-10-08 12:53:33
您需要bind()到本地IP地址,即您想要使用的接口的IP。
阅读有关bind()的指南
Beej Networking#bind指南
发布于 2013-10-08 09:21:45
在执行Rx/Tx之前,典型的TCP/UDP套接字绑定在特定的IP/端口上。尝试为您的方法使用原始套接字。
它的代码相当大,请参考下面的链接未加工
通过原始套接字-now创建一个原始套接字- Rx,您将通过原始套接字从L2 Tx接收整个帧-在修改相关字段后从L3报头进行传输。
https://stackoverflow.com/questions/19243406
复制相似问题