2010-11-16 2 views
11

Я пытаюсь создать сервер, к которому можно подключить несколько клиентов. Вот мой код до сих пор:C, программирование сокетов: подключение нескольких клиентов к серверу с помощью select()

Клиент:

int main(int argc, char **argv) { 

    struct sockaddr_in servaddr; 
    int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 

    if (sock == -1) perror("Socket"); 

    bzero((void *) &servaddr, sizeof(servaddr)); 
    servaddr.sin_family = AF_INET; 
    servaddr.sin_port = htons(6782); 
    servaddr.sin_addr.s_addr = inet_addr(<server_ip_address>); 

    if (-1 == connect(sock, (struct sockaddr *)&servaddr, sizeof(servaddr))) 
    perror("Connect"); 

    while(1) { 

    char message[6]; 
    fgets(message, 6, stdin); 

    message[5] = '\0'; 

    send(sock, message, 6, 0); 
    } 


    close(sock); 
} 

Сервер:

int main(int argc, char **argv) { 

    fd_set fds, readfds; 
    int i, clientaddrlen; 
    int clientsock[2], rc, numsocks = 0, maxsocks = 2; 

    int serversock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
    if (serversock == -1) perror("Socket"); 

    struct sockaddr_in serveraddr, clientaddr; 
    bzero(&serveraddr, sizeof(struct sockaddr_in)); 
    serveraddr.sin_family = AF_INET; 
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    serveraddr.sin_port = htons(6782); 

    if (-1 == bind(serversock, (struct sockaddr *)&serveraddr, 
       sizeof(struct sockaddr_in))) 
    perror("Bind"); 

    if (-1 == listen(serversock, SOMAXCONN)) 
    perror("Listen"); 

    FD_ZERO(&fds); 
    FD_SET(serversock, &fds); 

    while(1) { 

    readfds = fds; 
    rc = select(FD_SETSIZE, &readfds, NULL, NULL, NULL); 

    if (rc == -1) { 
     perror("Select"); 
     break; 
    } 

    for (i = 0; i < FD_SETSIZE; i++) { 
     if (FD_ISSET(i, &readfds)) { 
     if (i == serversock) { 
      if (numsocks < maxsocks) { 
      clientsock[numsocks] = accept(serversock, 
             (struct sockaddr *) &clientaddr, 
             (socklen_t *)&clientaddrlen); 
      if (clientsock[numsocks] == -1) perror("Accept"); 
      FD_SET(clientsock[numsocks], &fds); 
      numsocks++; 
      } else { 
      printf("Ran out of socket space.\n"); 

      } 
     } else { 
      int messageLength = 5; 
      char message[messageLength+1]; 
      int in, index = 0, limit = messageLength+1; 

      while ((in = recv(clientsock[i], &message[index], limit, 0)) > 0) { 
      index += in; 
      limit -= in; 
      } 

      printf("%d\n", index); 
      printf("%s\n", message); 

     } 
     } 
    } 
    } 

    close(serversock); 
    return 0; 
} 

Как только клиент подключается и отправляет свое первое сообщение, сервер просто работает в бесконечном цикле, и выплескивает мусор из массива сообщений. Кажется, что recv ничего не получает. Может ли кто-нибудь увидеть, где я ошибаюсь?

ответ

4

Два вопроса в вашем коде:

  • Вы должны сделать вместо recv(clientsock[i], ...)

  • После того, что вы не проверить, если recv() не удалось, и поэтому printf() распечатывает неинициализированных буфера message, следовательно, вывоз мусора на выходе

1

Вам необходимо проверить лимит < = 0 в вашем считываемом цикле, до, который вы называете прочитанным.

1

В цикле while для сервера измените код, чтобы сделать recv(i) вместо recv(clientsocks[i]). Я реализовал этот код и работает с этим изменением.

0

я заменил еще с ниже, и она работает

} else { 
/*     int messageLength = 5; 
        char message[messageLength+1]; 
        int in, index = 0, limit = messageLength+1; 

        memset (&message[index] , 0, sizeof (message [index])); 

        while ((in = recv(i, &message[index], limit, 0)) > 0) { 
         index += in; 
         limit -= in; 
        } 

        printf("%d\n", index); 
        printf("%s\n", message); 
*/ 
        bzero(buf, sizeof(buf)); 
        if ((rval = read(i, buf, 1024)) < 0) 
         perror("reading stream message"); 
        else if (rval == 0) 
         printf("Ending connection\n"); 
        else 
         printf("-->%s\n", buf); 

       } 
+0

Вам не нужно 'bzero()'. Просто правильно учтите возвращаемое значение recv(), например, в 'printf (" ->%. * S \ n ", rval, buf);'. – EJP

0

1) Это хорошая практика, чтобы использовать PF_INET (семейный протокол), а не
AF_INET (адрес семьи) во время создания сокета.

2) в то время как (1) петли
каждый раз, когда это целесообразно, чтобы сделать ваш readfds пустым, используя FD_ZERO (& readfds). в вызове recv() вы должны использовать i, а не clientsocks [i] вы должны проверить, что возвращаемое значение recv отрицательно (что указывает на ошибку при чтении), если это так, что вам не нужно печатать сообщение. во время печати сообщения убедитесь, что stdout/server готов для записи чего-либо, что вы можете сделать, используя writefds (третий аргумент select).