以下代码来自Git。它加入多播组并接收数据包。
在这里,我们循环并在名为msgbuf
的缓冲区中接收数据:
while (1)
{
char msgbuf[MSGBUFSIZE];
const int addrlen = sizeof(addr);
const int nbytes = recvfrom(fd, msgbuf, MSGBUFSIZE, 0, (struct sockaddr *) &addr, &addrlen);
如何选择缓冲区msgBuf
的大小?是否仅必须是最大数据包大小?还是在处理第一个数据包时需要存储多个数据包?
完整代码:
int main(int argc, char *argv[])
{
if (argc != 3) {
printf("Command line args should be multicast group and port\n");
printf("(e.g. for SSDP, `listener 239.255.255.250 1900`)\n");
return 1;
}
char* group = argv[1]; // e.g. 239.255.255.250 for SSDP
int port = atoi(argv[2]); // 0 if error, which is an invalid port
if(port <= 0)
{
perror("Invalid port");
return 1;
}
// create what looks like an ordinary UDP socket
//
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
{
perror("socket");
return 1;
}
// allow multiple sockets to use the same PORT number
//
u_int yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*) &yes, sizeof(yes)) < 0)
{
perror("Reusing ADDR failed");
return 1;
}
// set up destination address
//
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY); // differs from sender
addr.sin_port = htons(port);
// bind to receive address
//
if (bind(fd, (struct sockaddr*) &addr, sizeof(addr)) < 0)
{
perror("bind");
return 1;
}
// use setsockopt() to request that the kernel join a multicast group
//
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(group);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &mreq, sizeof(mreq)) < 0)
{
perror("setsockopt");
return 1;
}
// now just enter a read-print loop
//
while (1)
{
char msgbuf[MSGBUFSIZE];
const int addrlen = sizeof(addr);
const int nbytes = recvfrom(fd, msgbuf, MSGBUFSIZE, 0, (struct sockaddr *) &addr, &addrlen);
if (nbytes < 0)
{
perror("recvfrom");
return 1;
}
msgbuf[nbytes] = '\0';
puts(msgbuf);
}
return 0;
}
答案 0 :(得分:2)
与将数据包组合成流的TCP不同,UDP尊重数据包边界,因此recvfrom
一次只能获取一个数据包。
因此MSGBUFSIZE
仅需要与单个数据包一样大。如果您使用的不是巨型数据包,则为1500,否则为9000。
答案 1 :(得分:1)
@Ingo指出,在此代码中,您应该使用:
char msgbuf[MSGBUFSIZE + 1];
+ 1
是因为recvfrom
可以将多达MSGBUFSIZE
个字节写入数组,然后在末尾写入另一个NUL字节。
就为MSGBUFSIZE
选择一个值,这将取决于协议规范。考虑到大多数物理网络将很难发送超过1500个字节而不会产生碎片,例如2048可能是一个合理的值。您还可以检查nbytes == MSGBUFSIZE
(也许也使用MSG_TRUNC
)并报告“数据包被截断”警告,但是对于通过公用Internet路由的数据包基本上不会发生
响应:
处理第一个数据包时是否需要存储多个数据包?
通常,您应该让网络堆栈来处理。 recv
维持数据包/数据报的边界,因此将始终开始在提供的缓冲区地址处写入下一个数据包。再次取决于协议,您如何检测和处理错误,例如数据包丢失或顺序混乱以及超时