将数据发送到多个客户端 UDP 时不支持地址族

Address family not supported when sending data to multiple clients UDP

本文关键字:UDP 不支持 地址 客户端 数据      更新时间:2023-10-16

我目前正在用linux编写一个程序,其中:

  1. 客户端向服务器发送"密码">

  2. 服务器等待n个人发送密码并记录发件人地址

  3. 收到 N 条消息后,将向发件人发送启动消息。

问题是当我尝试将"开始"发送回客户端时,我收到非法查找错误(发送中的错误:非法查找(。并且只有第一个客户端收到启动消息(clientaddrs[0](

注意:经过 30 分钟的测试,错误现在变为:协议不支持地址系列。(代码中绝对没有任何变化(

这是我的代码(我粘贴了一个最小的可重现示例(

要重现此问题:

  1. 使用参数运行服务器代码:8080

  2. 在提示符下输入 2

  3. 在两个不同的参数上运行客户端代码:127.0.0.1 8080

  4. 选择两个不同的密码,并在客户端提示时输入它们

服务器代码:


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <chrono>
#include <errno.h>
#include <ifaddrs.h>
#define TO_CLI_BUF_SIZE 32
#define FROM_CLI_BUF_SIZE 8
void printAddr(){ // for printing my ip
struct ifaddrs * ifAddrStruct=NULL;
struct ifaddrs * ifa=NULL;
void * tmpAddrPtr=NULL;
getifaddrs(&ifAddrStruct);
for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr) {
continue;
}
if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4
// is a valid IP4 Address
tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
char addressBuffer[INET_ADDRSTRLEN];
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
printf("%s IP Address %sn", ifa->ifa_name, addressBuffer); 
} else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6
// is a valid IP6 Address
tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
char addressBuffer[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
printf("%s IP Address %sn", ifa->ifa_name, addressBuffer); 
} 
}
if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);
}

int main(int argc, char ** argv){
//seed rand
srand(time(NULL));
int sockfd; // socket
int port; // my port to listen on
struct sockaddr_in serveraddr; // server's address
struct sockaddr_in clientaddrs[4];
socklen_t clientLens[4];
int currentAddrMax = 0;
struct hostent * hostp; //host info
char * hostaddrp; // host adddr string
char toClientBuf[TO_CLI_BUF_SIZE];
char fromClientBuf[FROM_CLI_BUF_SIZE];

if(argc != 2){
perror("usage: file <port>");
exit(1);
}
port = atoi(argv[1]);
// create socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd<0){
perror("ERROR: opening socket.");
exit(1);
}
//int option = 1;
//setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&option, sizeof(int));

//internet stuff
bzero((char*) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){
perror("ERROR on bind");
exit(1);
}

int playerKeys[4];
int playerJoined[4];
printf("(you can enter 1,2,3 or 4)n");
printf("Enter amount of players: n");
int amountPlayers = 0;
scanf("%d",&amountPlayers);
// hacky way to clear screen
printf("33[H33[J");
printAddr();
printf("PORT: %dn", port);
printf("player| key| inn");
for(int i = 0; i < 4; i++){
bool keyExists = true;
while (keyExists == true ){
playerKeys[i] = rand()%10000;
keyExists = false;
for(int j = 0; j < i; j++){
if(playerKeys[i] == playerKeys[j]){
keyExists = true;
}
}
}
printf("%d     |%04d|",i+1, playerKeys[i]);
if(playerJoined[i] == 1){
printf(" on");
}else{
printf(" xn");
}
fflush(stdin);
} 
for(int i = 0; i < amountPlayers;i++){
bzero(fromClientBuf, FROM_CLI_BUF_SIZE);
int n = recvfrom(sockfd, fromClientBuf,FROM_CLI_BUF_SIZE, 0, (struct sockaddr*) &clientaddrs[currentAddrMax], &(clientLens[currentAddrMax]));
//TODO store senders 
if(n>0){
int key = (fromClientBuf[0]-'0')*1000; //TODO change the way keys are extracted.
key += (fromClientBuf[1]-'0')*100;
key += (fromClientBuf[2]-'0')*10;
key += (fromClientBuf[3]-'0');
for (int i = 0; i < 4; i++){
if(playerKeys[i] == key && playerJoined[i] == 0){
playerJoined[i] = 1;
currentAddrMax++;
}
}
printf("33[H33[J");
printAddr();
printf("PORT: %dn", port);
printf("player| key| inn");
for(int i = 0; i < 4; i++){
printf("%d     |%04d|",i+1, playerKeys[i]);
if(playerJoined[i] == 1){
printf(" on");
}else{
printf(" xn");
}
}
// decode key

}
}
//TODO finished waiting for all senders. send them start signal
//MAY BE USEFULL:n = sendto(sockfd, toClientBuf, strlen(toClientBuf), 0, (struct sockaddr *) &clientaddr, clientlen);
strcpy(toClientBuf, "start");
for(int j = 0; j < currentAddrMax; j++){
int n = sendto(sockfd, toClientBuf, strlen(toClientBuf), 0, (struct sockaddr *) &clientaddrs[j], (clientLens[j]));
if(n < 0) {
perror("ERROR in sendto");
printf("%dn",j);
exit(1);
}
}
// wait for connections
//main loop
//set some options 
struct timeval read_timeout;
read_timeout.tv_sec = 0;
read_timeout.tv_usec = 100;
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,&read_timeout,sizeof(read_timeout)) < 0) {
perror("Error with options");
}
printf("start loopn");

return 0;
}

客户端代码:

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

#define FROM_SER_BUF_SIZE 32
#define TO_SER_BUF_SIZE 8
int main(int argc, char **argv){
int sockfd, portno, n;
socklen_t serverlen;
struct sockaddr_in serveraddr;
struct hostent *server;
char *hostname;

char toServerBuf[TO_SER_BUF_SIZE];
char fromServerBuf[FROM_SER_BUF_SIZE];
if (argc != 3) {
perror("usage: filename <hostname> <port>n");
exit(0);
}
hostname = argv[1];
portno = atoi(argv[2]);
// create socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("ERROR: opening socketsn");
exit(0);
}
// get host
server = gethostbyname(hostname);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host as %sn", hostname);
exit(0);
}
// build server's internet address
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
bcopy((char *)server->h_addr, 
(char *)&serveraddr.sin_addr.s_addr, server->h_length);
serveraddr.sin_port = htons(portno);
bzero(toServerBuf, TO_SER_BUF_SIZE);
int key = 0;
printf("Please enter your key: ");
scanf("%d",&key);
if(key > 9999 || key < 0){
printf("INVALID KEYn");
exit(1);
}
toServerBuf[0] = key/1000 + '0';
toServerBuf[1] = (key%1000)/100 + '0';
toServerBuf[2] = (key%100)/10 +'0';
toServerBuf[3] = key%10 + '0';
serverlen = sizeof(serveraddr);
n = sendto(sockfd, toServerBuf, strlen(toServerBuf), 0, ( struct sockaddr *) &serveraddr, serverlen);
if (n < 0){
perror("ERROR: sendton");
exit(0);
}
//TODO wait for server to get send start signal
bzero(fromServerBuf, FROM_SER_BUF_SIZE);
n = recvfrom(sockfd, fromServerBuf, FROM_SER_BUF_SIZE, 0,( struct sockaddr *) &serveraddr, &serverlen);
printf("wow we got here");
return 0;
}

事实证明,这是 g++ 的问题。不知道为什么 g++ 不起作用,但是,一旦我切换到 clang 我所有的错误都消失了。但是,clang++也不起作用。

编辑:valgrind发现的clientaddr长度有问题。结果在客户端 legnths 的数组中(对于我的例子(,我们必须首先用

clientLens[currentAddrMax] = sizeof(clientaddrs[currentAddrMax]);

正如 Bodo 所提到的,我们必须始终初始化客户端长度,否则会出现未指定的行为。