2016-11-20 14 views
1

Я пытаюсь написать простой сервер сокетов, для этого примера это эхо-сервер.Почему `select` продолжает думать, что мне нужно« принять »?

Это socket_server.c (обратите внимание на комментарий о том, где он застревает)

int fdmax, i, socket_descriptor; 
fd_set master, read_fds; 
struct timeval tv = {.tv_sec = 0, .tv_usec = 1000}; 

void socket_server_init(void) 
{ 
    FD_ZERO(&master); 
    FD_ZERO(&read_fds); 
} 

socket_server_socket socket_server_start(char *socket_path) 
{ 
    struct sockaddr_un local; 
    int len; 
    int socket_descriptor = socket(AF_UNIX, SOCK_STREAM, 0); 

    if (socket_descriptor == -1) 
     return (socket_server_socket) {.status = SOCKET_SERVER_START_SOCKET_FAILURE, .descriptor = -1}; 

    local.sun_family = AF_UNIX; 
    strcpy(local.sun_path, socket_path); 
    unlink(local.sun_path); 
    len = strlen(local.sun_path) + sizeof(local.sun_family); 

    if (bind(socket_descriptor, (struct sockaddr *)&local, len) == -1) 
     return (socket_server_socket) {.status = SOCKET_SERVER_START_BIND_FAILURE, .descriptor = -1}; 

    if (listen(socket_descriptor, 5) == -1) 
     return (socket_server_socket) {.status = SOCKET_SERVER_START_LISTEN_FAILURE, .descriptor = -1}; 

    FD_SET(socket_descriptor, &master); 
    fdmax = socket_descriptor; 
    return (socket_server_socket) {.status = 0, .descriptor = socket_descriptor}; 
} 

socket_server_socket socket_server_wait_for_connection(socket_server_socket server) 
{ 
    read_fds = master; 
    select(fdmax+1, &read_fds, NULL, NULL, &tv); 

    for (i = 0; i <= fdmax; i++) 
    { 
     if (FD_ISSET(i, &read_fds)) 
     { 
      if (i == server.descriptor) 
      { 
       // It's getting stuck here. 
       socket_descriptor = accept(server.descriptor, (struct sockaddr *) NULL, NULL); 

       if (socket_descriptor > fdmax) 
        fdmax = socket_descriptor; 

       FD_SET(socket_descriptor, &master); 
       return (socket_server_socket) {.status = -1, .descriptor = socket_descriptor}; 
      } else { 
       return (socket_server_socket) {.status = 0, .descriptor = i}; 
      } 
     } 
    } 

    return (socket_server_socket) {.status = -1, .descriptor = -1}; 
} 

int socket_server_update(socket_server_socket client) 
{ 
    char buffer[256]; 
    int n = recv(client.descriptor, buffer, 256, 0); 
    if (n < 0) 
     return SOCKET_SERVER_UPDATE_RECV_FAILURE; 

    if (send(client.descriptor, buffer, n, 0) < 0) 
     return SOCKET_SERVER_UPDATE_SEND_FAILURE; 

    close(client.descriptor); 
    return 0; 
} 

Тогда в моей основной программе:

socket_server_init(); 
    socket_server_socket server = socket_server_start(SOCKET_PATH); 
    while (1) { 
     printf("wait for conn\n"); 
     socket_server_socket client = socket_server_wait_for_connection(server); 

     if (client.status == 0) 
     { 
      socket_server_update(client); 
     } 

     sleep(1); 
     printf("%d: Log !\n", (int)time(NULL)); 
    } 

Когда я запускаю программу, я наблюдаю:

  1. «Войдите!» выводится на консоль
  2. Я подключаюсь к серверу через розетку
  3. Продолжаю просмотр «Log!». на консоли
  4. Я послал некоторые данные от клиента
  5. Клиент видит данные эхо назад
  6. «Войти!» больше не рассматривается на консоли
  7. Любые последующие данные от клиента не отражаемый назад

Я ожидаю, что петля продолжать и «Log!» для продолжения вывода, но похоже, что моя программа застревает по вызову accept, но только во второй раз.

Как я понимаю, select должен только добавить дескриптор в read_fds, если его необходимо принять или отобрать. Так что, кажется, происходит это:

  1. select добавляет дескриптор сервера в read_fds
  2. Он получает принято
  3. select снова добавляет дескриптор серверов read_fds
  4. Там ничто ждет, чтобы быть accept ред так accept зависает

Я проверял, что оба раза, когда accept, дескриптор тот же. Поэтому я очень смущен.

Что я делаю неправильно? Я уверен, что он не должен бить accept второй раз для того же соединения.

ответ

1

Вы используете таймаут, если этот тайм-аут пройдет, select(2) вернет 0, ваша программа не заботится о ее возвращаемом значении и относится к ней как к успешному звонку («У нас есть что прочитать»), но read_fds hasn ' t обновлено. Поэтому он все еще сохраняет свое предыдущее значение («У нас новый клиент»), затем вы вызываете accept и застреваете.

решения было бы очень легко: проверить select(2) «s возвращаемого значения:

int result = select(fdmax+1, &read_fds, NULL, NULL, &tv); 
if (result < 0) { 
    // Handle error. 
} else if (result == 0) { 
    // Handle timeout. 
} 

Чтобы узнать больше о select и розетках в UNIX в общем, я настоятельно рекомендую прочитать Beej's Guide to Network Programming.

+0

Это кажется близким ... Я не могу следовать вашему предложению, потому что это вызывает блокировку, пока он ждет соединения. Я использую тайм-аут, чтобы другие части моей программы могли продолжать работать. Я попробовал 'if (select (fdmax + 1, & read_fds, NULL, NULL, & tv) <0) return;', но потом я попал в ситуацию, когда 'select' просто продолжает возвращать -1 по какой-то причине. –

+0

Имейте в виду, что вы должны иметь недопустимые (закрытые) сокеты 'FD_CLR' из вашего' fd_set'. , Попробуйте следовать примеру Beej для простого чата и сравнить его с вашей программой. Чтобы узнать, почему select возвращает -1, попробуйте напечатать последнюю ошибку с помощью 'perror'. –

+0

@CameronBall, если select возвращает 0, у вас есть тайм-аут. Если он возвращает <0, у вас есть * ошибка *, и вам нужно проверить errno и/или использовать perror или что-то еще в irder, чтобы узнать, что такое ошибка. –

 Смежные вопросы

  • Нет связанных вопросов^_^