У меня есть приложение, которое очень долго работало в разных дистрибутивах под ядром v2, v3 и v4, и у меня никогда не было проблем. Сейчас я пытаюсь заставить его работать на Ubuntu с ядром V5, и у меня есть проблема. Это основано на сокетах UDP и есть механизм обнаружения, который использует широковещательные пакеты. Как это работает, клиент отправляет широковещательный пакет UPD, и все серверы отвечают на эту широковещательную рассылку. После этого клиент получает информацию с каждого сервера, отправляя направленный пакет UPD на все серверы, которые ответили на трансляцию. На стороне сервера, когда получен пакет UDP, я вызываю connect с адресом клиента, поэтому мне не нужно больше следовать адресу клиента и порту в коде. Как только ответ отправлен, я «разъединяю» сокет, вызывая connect с AF_UNSPE C sin_family.
Это работало нормально, но не с ядром V5. После того, как я отключился, кажется, что сервер не получает направленных сообщений. Он получает широковещательный UPD, но не тот, который специально адресован к нему.
На всякий случай причиной проблемы могут быть другие факторы (firewall, et c ...), я сделал небольшой проверить код и увидеть то же самое; этот код включен в мой пост. Если я не использую подключение / отключение в коде сервера, он работает нормально. Но не с подключением / отключением. И если я использую ядро 4 и ниже, он прекрасно работает с подключением / отключением. Для ядра V5 я тестировал на 5.2.9 и 5.3.
Я не понимаю, потому что это правильный и документированный способ работы с сокетами UDP. Я много гуглил и не нашел ничего такого, что указывало бы на то, что оно не работает с Linux ядром V5.x.
Кто-нибудь имеет представление об этой проблеме?
Спасибо.
Фрэнк
Код сервера
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <errno.h>
#define PORT 20000
#define MAXLINE 512
// Driver code
int main() {
int sockfd;
char buffer[MAXLINE];
char ifName[50] = "enp2s0";
struct sockaddr_in servaddr, cliaddr;
char resp[50];
fd_set rset;
struct sockaddr hwaddr;
struct sockaddr_in curr_ipaddr, ipaddr;
struct sockaddr_in udp_addr;
struct timeval toval, *ptoval;
int maxfdp1, errcode;
int on = 1;
resp[0] = 0xaa;
resp[1] = 0x55;
resp[2] = 0x80;
resp[3] = 0x7f;
resp[4] = 0xd0;
resp[5] = 0x00;
resp[6] = 0x09;
resp[7] = 0xaa;
resp[8] = 0x00;
resp[9] = 0x02;
resp[10] = 0x2c;
resp[11] = 0x08;
resp[12] = 0x00;
resp[13] = 0x40;
resp[14] = 0x10;
resp[15] = 0x92;
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// Allow broadcast
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
memset(&servaddr, 0, sizeof(servaddr));
memset(&cliaddr, 0, sizeof(cliaddr));
// Filling server information
servaddr.sin_family = AF_INET; // IPv4
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(PORT);
// Bind the socket with the server address
if ( bind(sockfd, (const struct sockaddr *)&servaddr,
sizeof(servaddr)) < 0 )
{
perror("bind failed");
exit(EXIT_FAILURE);
}
int len, n;
len = sizeof(cliaddr); //len is value/resuslt
maxfdp1 = sockfd+1;
FD_ZERO(&rset);
for ( ; ; )
{
FD_SET(sockfd, &rset);
toval.tv_sec = 0;
toval.tv_usec = 500000;
ptoval = &toval;
if ((errcode = select(maxfdp1, &rset, NULL, NULL, ptoval)) < 0)
{
if (errno == EINTR)
{
continue;
}
else
{
printf("Select error %d", errno);
}
}
if (FD_ISSET(sockfd, &rset))
{
n = recvfrom(sockfd, (char *)buffer, MAXLINE,
MSG_WAITALL, ( struct sockaddr *) &cliaddr,
&len);
buffer[n] = '\0';
printf("received =\n");
for (int i=0; i<n; i++) printf("%02x ", (unsigned char)buffer[i]);
printf("\n");
// Connect the socket; this way it can be used as a TCP socket
connect(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
if ((unsigned char)buffer[4] == 0xc0)
{
// This is to answerr to the broadcast so I can receive the
// the next directed packet
printf("sending ans\n");
sendto(sockfd, (const char *)resp, 16,
MSG_CONFIRM, (const struct sockaddr *) &cliaddr,
len);
}
// Unconnect the UDP socket
struct sockaddr_in dumaddr;
// Filling server information
dumaddr.sin_family = AF_UNSPEC; // IPv4
dumaddr.sin_addr.s_addr = INADDR_ANY;
dumaddr.sin_port = htons(PORT);
int cerr = connect(sockfd, (struct sockaddr *)&dumaddr, sizeof(dumaddr));
printf("cerr = %d\n", cerr);
}
}
return 0;
}
Код клиента
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <errno.h>
#define PORT 20000
#define MAXLINE 512
// Driver code
int main() {
int sockfd;
char buffer[MAXLINE];
struct sockaddr_in servaddr, cliaddr;
char cmdc0[20], cmdc1[20];
int on = 1;
cmdc0[0] = 0xaa;
cmdc0[1] = 0x55;
cmdc0[2] = 0x80;
cmdc0[3] = 0x7f;
cmdc0[4] = 0xc0;
cmdc0[5] = 0x00;
cmdc0[6] = 0x00;
cmdc0[7] = 0x00;
cmdc0[8] = 0x00;
cmdc0[9] = 0x00;
cmdc1[0] = 0xaa;
cmdc1[1] = 0x55;
cmdc1[2] = 0x80;
cmdc1[3] = 0x7f;
cmdc1[4] = 0xc1;
cmdc1[5] = 0x00;
cmdc1[6] = 0x00;
cmdc1[7] = 0x00;
cmdc1[8] = 0x00;
cmdc1[9] = 0x00;
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// Allow broadcast
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
memset(&servaddr, 0, sizeof(servaddr));
memset(&cliaddr, 0, sizeof(cliaddr));
// Filling server information
servaddr.sin_family = AF_INET; // IPv4
servaddr.sin_addr.s_addr = INADDR_BROADCAST;
servaddr.sin_port = htons(PORT);
int len, n;
len = sizeof(servaddr); //len is value/resuslt
sendto(sockfd, (const char *)cmdc0, 7,
MSG_CONFIRM, (const struct sockaddr *) &servaddr,
len);
while (1 == 1)
{
int clen=sizeof(cliaddr);
n = recvfrom(sockfd, buffer, MAXLINE-1, 0, (struct sockaddr *)&cliaddr, &clen);
buffer[n] = '\0';
char *baddr=(char *)&(cliaddr.sin_addr);
printf("received from = %d-%d-%d-%d\n", (unsigned char)baddr[0], (unsigned char)baddr[1],
(unsigned char)baddr[2], (unsigned char)baddr[3]);
for (int i=0; i<n; i++) printf("%02x ", (unsigned char)buffer[i]);
printf("\n");
if ((unsigned char)buffer[4] == 0xd0)
{
// Send another command but directed this time
printf("Sending C1...\n");
sendto(sockfd, cmdc1, 7, 0, (const struct sockaddr *)&cliaddr, clen);
}
}
return 0;
}