在Linux平台上,我想在两个进程之间使用套接字共享。其中一个进程在套接字上发送数据,另一个进程接收数据。我在这个网站(here)上读到这是通过设置SO_REUSEADDR和/或SO_REUSEPORT选项来完成的。
所以我设置了一个包含3个过程的测试场景:
1)绑定到localhost的echo服务器侦听127.0.0.1:44000上的消息。收到消息后,它会立即回复发件人;
2)绑定到127.0.01:44001的发件人并向echo服务器发出定期消息;
3)接收器绑定到127.0.01:44001并侦听来自echo服务器的消息;
问题:Receiver停止接收来自echo服务器的回复。这取决于使用的套接字选项:
使用SO_REUSEADDR: 如果发送方(2)在接收方(3)之后启动,则接收方(3)不接收任何内容。如果接收器最后启动,但发送器重新启动,则接收器再次停止接收。
使用SO_REUSEPORT(或与SO_REUSEADDR一起使用): 情况正好相反 - 接收器必须首先启动以便工作,因为发送器最后启动,您可以根据需要多次重启发送器,一切都会正常工作。但是如果你重新启动发送者(或者只是最后启动它),它将不会收到任何消息。
这是我使用的代码:
#define CC_LISTEN_PORT 44000
#define DRN_LISTEN_PORT 44001
static void runCC_EchoMode(struct sockaddr_in* ccaddr)
{
char buf[100];
int s = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in remaddr;
int recvlen, sentlen;
// bind
if(bind(s, (struct sockaddr *)ccaddr, sizeof(struct sockaddr_in)) < 0)
{
debug("%s: bind failed", __func__);
return;
}
/* now loop, receiving data and printing what we received */
unsigned int addrlen = sizeof(remaddr);
int count = 0;
for (;;) {
debug("waiting on port %d\n", ntohs(ccaddr->sin_port));
recvlen = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&remaddr, &addrlen);
debug("received %d bytes\n", recvlen);
if (recvlen > 0) {
buf[recvlen] = 0;
printf("received message: \"%s\"\n", buf);
// send echo back
sprintf(buf, "Echo #%d", count++);
sentlen = sendto(s, buf, strlen(buf), 0, (struct sockaddr *)&remaddr, sizeof(remaddr));
debug("sent %d bytes to %s:%d\n", sentlen,
inet_ntoa(remaddr.sin_addr), ntohs(remaddr.sin_port));
}
}
close(s);
}
static void runDrn_SendMode(struct sockaddr_in* ccaddr, struct sockaddr_in* drnaddr)
{
char buf[100];
int s = socket(AF_INET, SOCK_DGRAM, 0);
int sentlen;
int one = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)) < 0) {
debug("setsockopt(SO_REUSEADDR) failed\n");
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(int)) < 0) {
debug("setsockopt(SO_REUSEPORT) failed\n");
}
// bind
if(bind(s, (struct sockaddr *)drnaddr, sizeof(struct sockaddr_in)) < 0)
{
debug("%s: bind failed", __func__);
return;
}
int count = 0;
for (;;) {
sleep(2);
sprintf(buf, "Hello #%d", count++);
debug("sending \"%s\" to server...\n", buf);
sentlen = sendto(s, buf, strlen(buf), 0, (struct sockaddr *)ccaddr, sizeof(struct sockaddr_in));
debug("sent %d bytes\n", sentlen);
}
close(s);
}
static void runDrn_RcvMode(struct sockaddr_in* ccaddr, struct sockaddr_in* drnaddr)
{
char buf[100];
int s = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in remaddr;
int recvlen;
int one = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)) < 0) {
debug("setsockopt(SO_REUSEADDR) failed\n");
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(int)) < 0) {
debug("setsockopt(SO_REUSEPORT) failed\n");
}
// bind
if(bind(s, (struct sockaddr *)drnaddr, sizeof(struct sockaddr_in)) < 0)
{
debug("%s: bind failed", __func__);
return;
}
/* now loop, receiving data and printing what we received */
unsigned int addrlen = sizeof(remaddr);
for (;;) {
debug("waiting on port %d\n", ntohs(drnaddr->sin_port));
recvlen = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *)&remaddr, &addrlen);
debug("received %d bytes\n", recvlen);
if (recvlen > 0) {
buf[recvlen] = 0;
printf("received message: \"%s\"\n", buf);
}
}
close(s);
}
int main(int argc, char *argv[])
{
int mode;
if (argc < 3) {
fprintf(stderr, "Usage: %s <host> <mode>\n", argv[0]);
exit(EXIT_FAILURE);
}
printf("Starting process with PID: %d\n", getpid());
// this is a simple wrapper of getaddrinfo()
AddressResolver resv1(argv[1]);
resv1.print();
struct sockaddr_in ccaddr, drnaddr;
ccaddr = *(resv1.getAddress(0));
ccaddr.sin_port = htons(CC_LISTEN_PORT);
drnaddr = *(resv1.getAddress(0));
drnaddr.sin_port = htons(DRN_LISTEN_PORT);
mode = atoi(argv[2]);
switch(mode) {
case 0: // cc
runCC_EchoMode(&ccaddr);
break;
case 1: // drone sender
runDrn_SendMode(&ccaddr, &drnaddr);
break;
case 2: // drone receiver
runDrn_RcvMode(&ccaddr, &drnaddr);
break;
default:
debug("Mode is not available\n");
break;
}
return 0;
}
这就是我开始3个过程的方式:
./testUDP localhost 0
./testUDP localhost 1
./testUDP localhost 2
这是测试运行的输出:
./testUDP localhost 0
Starting process with PID: 10651
IP: 127.0.0.1
waiting on port 44000
received 8 bytes
received message: "Hello #0"
sent 7 bytes to 127.0.0.1:44001
waiting on port 44000
received 8 bytes
received message: "Hello #1"
sent 7 bytes to 127.0.0.1:44001
waiting on port 44000
received 8 bytes
received message: "Hello #2"
sent 7 bytes to 127.0.0.1:44001
waiting on port 44000
^C
...
./testUDP localhost 1
Starting process with PID: 10655
IP: 127.0.0.1
sending "Hello #0" to server...
sent 8 bytes
sending "Hello #1" to server...
sent 8 bytes
sending "Hello #2" to server...
sent 8 bytes
^C
...
./testUDP localhost 2
Starting process with PID: 10652
IP: 127.0.0.1
waiting on port 44001
received 7 bytes
received message: "Echo #0"
waiting on port 44001
received 7 bytes
received message: "Echo #1"
waiting on port 44001
received 7 bytes
received message: "Echo #2"
waiting on port 44001
^C
答案 0 :(得分:2)
侦听同一接口和端口的两个不同进程的行为是不确定的:它会因操作系统,内核版本和其他因素而异。
通常,端口旨在与单个进程和套接字关联。 SO_REUSE
用于UDP多播接收或TCP在连接断开后绕过WAIT状态。虽然某些系统允许您将单个端口绑定到多个套接字,线程或进程以用于其他目的,但行为太多而无法使用。
您可能正在寻找的是某种数据包复制或循环分发。 SO_REUSE
不保证。