Почему recvfrom заботится о том, откуда поступают данные
Итак, я создаю сервер на C, который использует UDP, и я хочу прослушивать входящие пакеты из многих источников. Поэтому, когда я звоню ssize_t recvfrom(int, void *, size_t, int, struct sockaddr * __restrict, socklen_t * __restrict)
5-й параметр, содержащий информацию об отправителе, может отличаться.
Есть ли способ получить пакеты, не зная информацию об адресе каждого отдельного клиента? И возможно ли это с библиотекой C?
Вот мой код:
int file_descriptor;
char data[1024];
int bytes_recved;
sockaddr_in iDontKnow;
socklen_t addr_len = sizeof(iDontKnow);
if ((bytes_recved = recvfrom(file_descriptor, data, strlen(data), 0, (struct sockaddr*)&iDontKnow, &addr_len)) < 0) {
perror("Failed to receive data");
}
Я заметил, что при получении данных с помощью классов Java DatagramSocket и DatagramPacket, DatagramSocket receive
Функция принимает параметр типа DatagramPacket. Этот DatagramPacket, однако, содержал только объект, в который нужно поместить данные. Итак, почему реализация C получения UDP требует, чтобы вы знали информацию отправителя?
4 ответа
Есть ли способ получить пакеты, не зная информацию об адресе каждого отдельного клиента?
Ну, в любом случае, вам не нужно заранее знать информацию об отправителе. Как только пакет получен, информация об отправителе (если имеется) будет сохранена в address
,
Со страницы руководства,
ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len);
[...] Если аргумент address не является нулевым указателем, и протокол предоставляет адрес источника сообщений, адрес источника принятого сообщения должен быть сохранен в
sockaddr
структура, на которую указывает аргумент адреса, а длина этого адреса должна храниться в объекте, на который указываетaddress_len
аргумент.
Что касается части " почему", то в случае сокетов без установления соединения, если вы не знаете адрес отправителя для пакета в сообщении, вы не можете отвечать или отвечать отправителю. Таким образом, необходимо знать информацию об отправителе, особенно в режиме без установления соединения, и приходит recvfrom()
который, наряду с полученными данными, также дает нам информацию об отправителе.
РЕДАКТИРОВАТЬ:
В вашем коде
recvfrom(file_descriptor, data, strlen(data), 0, (struct sockaddr*)&iDontKnow, &addr_len)
неправильно, как strlen(data)
UB, так как вы пытаетесь посчитать длину неинициализированного массива char, который не может быть строкой. Это вызывает неопределенное поведение. Вы можете использовать sizeof(data)
, как data
это массив.
Если вас не интересует информация отправителя, просто передайте NULL
в качестве соответствующего аргумента.
Чтобы добавить к этому, для сокетов без установления соединения (UDP), фактически требуется получить информацию об отправителе. Для сокетов, ориентированных на соединение, у вас есть другая урезанная альтернатива, recv()
который только заботится о получении и хранении данных.
Вы сравниваете различные функции из Java и C. В C также есть recv()
функция, которая не предоставляет никакого адреса. Единственная цель recvfrom
над recv
чтобы получить адрес отправителя. Обычно серверы отвечают на пакеты, которые они получают. Без адреса, который невозможен.
Если вы не заботитесь об отправителе ваших пакетов, просто возьмите recv
,
Или, наоборот, если вы не заботитесь об отправителе, почему вы выбрали recvfrom
версия recv
?
Интересно, что делает сервер, если сервер не заботится об адресах клиента... Но это не связано с вашим вопросом.
это
DatagramPacket
Однако удерживается только объект, в который необходимо поместить данные.
И адрес источника и порт, и длина данных.
Итак, почему реализация C получения UDP требует, чтобы вы знали информацию отправителя?
Это не так. Он имеет возможность сообщить вам адрес источника и порт. Это параметр результата, а не вход.
Вы могли бы сделать это так,
int sockfd_recv;
struct sockaddr_in recvaddr;
bzero(&recvaddr, sizeof(recvaddr));
recvaddr.sin_family = AF_INET;
recvaddr.sin_port = htons(port_recv);
recvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
int ret = bind(sockfd_recv, (struct sockaddr *)&recvaddr, sizeof(recvaddr));