gpt4 book ai didi

c - Linux 上的 UDP connect() 和 recv()

转载 作者:IT王子 更新时间:2023-10-29 01:06:22 24 4
gpt4 key购买 nike

根据 connect(2) 手册页

If the socket sockfd is of type SOCK_DGRAM then serv_addr is the address to which datagrams are sent by default, and the only address from which datagrams are received. If the socket is of type SOCK_STREAM or SOCK_SEQPACKET, this call attempts to make a connection to the socket that is bound to the address specified by serv_addr.

我正在尝试过滤来自在同一端口上广播的两个不同多播组的数据包,我认为 connect() 可以完成这项工作,但我无法让它工作。事实上,当我将它添加到我的程序中时,我没有收到任何数据包。更多信息在此 thread .

这是我设置连接参数的方式:

memset(&mc_addr, 0, sizeof(mc_addr));
mc_addr.sin_family = AF_INET;
mc_addr.sin_addr.s_addr = inet_addr(multicast_addr);
mc_addr.sin_port = htons(multicast_port);
printf("Connecting...\n");
if( connect(sd, (struct sockaddr*)&mc_addr, sizeof(mc_addr)) < 0 ) {
perror("connect");
return -1;
}

printf("Receiving...\n");
while( (len = recv(sd, msg_buf, sizeof(msg_buf), 0)) > 0 )
printf("Received %d bytes\n", len);

最佳答案

您的程序(可能)存在以下问题:

  • 您应该使用 bind() 而不是 connect(),并且
  • 您缺少 setsockopt(..., IP_ADD_MEMBERSHIP, ...)。

这是一个接收多播的示例程序。它使用 recvfrom(),而不是 recv(),但它是相同的,除了您还获取每个接收到的数据包的源地址。

要从多个多播组接收,您有三个选项。

第一个选项:为每个多播组使用一个单独的套接字,并将每个套接字绑定(bind)到一个多播地址。这是最简单的选项。

第二个选项:为每个多播组使用单独的套接字,bind() 每个套接字 INADDR_ANY,并使用套接字过滤器过滤掉除单个多播组之外的所有套接字。

因为您已经绑定(bind)到 INADDR_ANY,您可能仍会收到其他多播组的数据包。可以使用内核的套接字过滤器将它们过滤掉:

#include <stdint.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/filter.h>

/**
* Adds a Linux socket filter to a socket so that only IP
* packets with the given destination IP address will pass.
* dst_addr is in network byte order.
*/
int add_ip_dst_filter (int fd, uint32_t dst_addr)
{
uint16_t hi = ntohl(dst_addr) >> 16;
uint16_t lo = ntohl(dst_addr) & 0xFFFF;

struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, SKF_NET_OFF + 16), // A <- IP dst high
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, hi, 0, 3), // if A != hi, goto ignore
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, SKF_NET_OFF + 18), // A <- IP dst low
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, lo, 0, 1), // if A != lo, goto ignore
BPF_STMT(BPF_RET + BPF_K, 65535), // accept
BPF_STMT(BPF_RET + BPF_K, 0) // ignore
};

struct sock_fprog fprog = {
.len = sizeof(filter) / sizeof(filter[0]),
.filter = filter
};

return setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
}

第三种选择:使用单个套接字接收所有多播组的多播。

在这种情况下,您应该为每个组执行 IP_ADD_MEMBERSHIP。这样您就可以在一个套接字上获取所有数据包。

但是,您需要额外的代码来确定接收到的数据包被寻址到哪个多播组。为此,您必须:

  • 使用recvmsg() 接收数据包并读取 IP_PKTINFO 或等效的辅助数据消息。然而,要让 recvmsg() 给你这个消息,你首先必须
  • 使用 setsockopt() 启用 IP_PKTINFO 辅助数据消息的接收。

您需要做的具体事情取决于 IP 协议(protocol)版本和操作系统。这是我的做法(IPv6 代码未测试):enabling PKTINFOreading the option .

这是一个接收多播的简单程序,它演示了第一个选项(绑定(bind)到多播地址)。

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define MAXBUFSIZE 65536

int main (int argc, char **argv)
{
if (argc != 4) {
printf("Usage: %s <group address> <port> <interface address>\n", argv[0]);
return 1;
}

int sock, status, socklen;
char buffer[MAXBUFSIZE+1];
struct sockaddr_in saddr;
struct ip_mreq imreq;

// set content of struct saddr and imreq to zero
memset(&saddr, 0, sizeof(struct sockaddr_in));
memset(&imreq, 0, sizeof(struct ip_mreq));

// open a UDP socket
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("socket failed!");
return 1;
}

// join group
imreq.imr_multiaddr.s_addr = inet_addr(argv[1]);
imreq.imr_interface.s_addr = inet_addr(argv[3]);
status = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(const void *)&imreq, sizeof(struct ip_mreq));

saddr.sin_family = PF_INET;
saddr.sin_port = htons(atoi(argv[2]));
saddr.sin_addr.s_addr = inet_addr(argv[1]);
status = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
if (status < 0) {
perror("bind failed!");
return 1;
}

// receive packets from socket
while (1) {
socklen = sizeof(saddr);
status = recvfrom(sock, buffer, MAXBUFSIZE, 0, (struct sockaddr *)&saddr, &socklen);
if (status < 0) {
printf("recvfrom failed!\n");
return 1;
}

buffer[status] = '\0';
printf("Received: '%s'\n", buffer);
}
}

关于c - Linux 上的 UDP connect() 和 recv(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8481873/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com