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

Address family not supported when sending data to multiple clients UDP

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

  1. 客户端向服务器发送一个"password"

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

  3. 收到n条消息后,发送一条起始消息给发送者。

问题是当我尝试将 "start" 发送回客户端时出现非法搜索错误(发送到错误:非法搜索)。并且只有第一个客户端收到启动消息(clientaddrs[0])

注意:经过30分钟的测试,错误现在变成:Address family not supported by protocol。(代码中绝对没有任何变化)

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

重现问题:

  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 %s\n", 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 %s\n", 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("3[H3[J");
  printAddr();
  printf("PORT: %d\n", port);
  printf("player| key| in\n");
  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(" o\n");
    }else{
      printf(" x\n");
    }
    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("3[H3[J");
      printAddr();
      printf("PORT: %d\n", port);
      printf("player| key| in\n");
      for(int i = 0; i < 4; i++){
        printf("%d     |%04d|",i+1, playerKeys[i]);
        if(playerJoined[i] == 1){
          printf(" o\n");
        }else{
          printf(" x\n");
        }
      }
      // 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("%d\n",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 loop\n");


  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 sockets\n");
    exit(0);
  }

  // get host
  server = gethostbyname(hostname);
  if (server == NULL) {
    fprintf(stderr,"ERROR, no such host as %s\n", 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 KEY\n");
    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: sendto\n");
    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 长度有问题。结果在客户端长度的数组中(以我的示例为例),我们必须首先使用

行对其进行初始化

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

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