【发布时间】:2014-10-13 17:06:04
【问题描述】:
我正在 OS X (10.9.4) 上编写一个非常初学者的 UDP 套接字代码,它使用多个绑定到相同 IP 地址和端口的 UDP 套接字。我认为使用SO_REUSEPORT 可以让我通过多个套接字接收数据包。但是在下面的例子中,只有两个套接字中的一个可以接收数据(即client2,它将一个套接字绑定到client1之后的端口)。
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <unistd.h>
#include <string>
#include <iostream>
class UDP
{
private:
uint32_t fSocket;
struct sockaddr_in fSourceSocketAddress;
struct sockaddr_in fDestinationSocketAddress;
public:
UDP(const std::string& source_host, uint16_t source_port,
const std::string& dest_host, uint16_t dest_port);
void Send(const std::string& str);
std::string Receive();
};
//______________________________________________________________________________
UDP::UDP(const std::string& source_host, uint16_t source_port,
const std::string& dest_host, uint16_t dest_port)
{
fSocket = socket(AF_INET, SOCK_DGRAM, 0);
int on = 1;
errno = 0;
setsockopt(fSocket, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on));
memset(&fDestinationSocketAddress, 0, sizeof(fDestinationSocketAddress));
fDestinationSocketAddress.sin_addr.s_addr = inet_addr(dest_host.c_str());
fDestinationSocketAddress.sin_port = htons(dest_port);
fDestinationSocketAddress.sin_family = AF_INET;
memset(&fSourceSocketAddress, 0, sizeof(fSourceSocketAddress));
fSourceSocketAddress.sin_addr.s_addr = inet_addr(source_host.c_str());
fSourceSocketAddress.sin_port = htons(source_port);
fSourceSocketAddress.sin_family = AF_INET;
errno = 0;
bind(fSocket, (struct sockaddr*)&fSourceSocketAddress, sizeof(fSourceSocketAddress));
}
//______________________________________________________________________________
void UDP::Send(const std::string& str)
{
char buffer[100];
strcpy(buffer, str.c_str());
sendto(fSocket, buffer, str.length(), 0, (struct sockaddr*)&fDestinationSocketAddress, sizeof(fDestinationSocketAddress));
}
//______________________________________________________________________________
std::string UDP::Receive()
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(fSocket, &fds);
struct timeval tv = {1, 0};
int nselect = select(fSocket + 1, &fds, NULL, NULL, &tv);
if(nselect == 0 or !FD_ISSET(fSocket, &fds)){
return "";
} // if
char buffer[100];
recvfrom(fSocket, buffer, 99, 0, NULL, NULL);
buffer[99] = '\0';
return std::string(buffer);
}
//______________________________________________________________________________
int main()
{
UDP client1("127.0.0.1", 8000, "127.0.0.1", 8001);
UDP client2("127.0.0.1", 8000, "127.0.0.1", 8001);
UDP server ("127.0.0.1", 8001, "127.0.0.1", 8000);
for(int i = 0; i < 10; i++){
server.Send("Hello");
} // i
for(int i = 0; i < 10; i++){
std::cout << "Client 1 received: " << client1.Receive() << std::endl;
std::cout << "Client 2 received: " << client2.Receive() << std::endl;
} // i
return 0;
}
这是此示例代码的结果。
$ ./a.out
Client 1 received:
Client 2 received: Hello
Client 1 received:
Client 2 received: Hello
Client 1 received:
Client 2 received: Hello
Client 1 received:
Client 2 received: Hello
Client 1 received:
Client 2 received: Hello
Client 1 received:
Client 2 received: Hello
Client 1 received:
Client 2 received: Hello
Client 1 received:
Client 2 received: Hello
Client 1 received:
Client 2 received: Hello
Client 1 received:
Client 2 received: Hello
但我的预期结果如下。
$ ./a.out
Client 1 received: Hello
Client 2 received: Hello
Client 1 received: Hello
Client 2 received: Hello
Client 1 received: Hello
Client 2 received: Hello
Client 1 received: Hello
Client 2 received: Hello
Client 1 received: Hello
Client 2 received: Hello
Client 1 received:
Client 2 received:
(snip)
如何让两个套接字都接收到“Hello”?我还想让代码与 Linux(内核 2.4)兼容。我尝试在 Linux 上使用 SO_REUSEADDR 而不是 SO_REUSEPORT,但得到的结果与在 OS X 上相同。
【问题讨论】: